diff options
65 files changed, 22300 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea0baf2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +*.swp +*.bak @@ -0,0 +1,49 @@ +Copyright (c) 2013, SEMTECH S.A. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Semtech corporation nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +--- For the parson library used by the packet logger --- + +Parson ( http://kgabis.github.com/parson/ ) +Copyright (c) 2012 Krzysztof Gabis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +ITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3a1c041 --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +### Environment constants + +ARCH ?= +CROSS_COMPILE ?= +export + +### general build targets + +all: + $(MAKE) all -e -C libloragw + $(MAKE) all -e -C util_pkt_logger + $(MAKE) all -e -C util_spi_stress + $(MAKE) all -e -C util_tx_test + $(MAKE) all -e -C util_lbt_test + $(MAKE) all -e -C util_tx_continuous + $(MAKE) all -e -C util_spectral_scan + +clean: + $(MAKE) clean -e -C libloragw + $(MAKE) clean -e -C util_pkt_logger + $(MAKE) clean -e -C util_spi_stress + $(MAKE) clean -e -C util_tx_test + $(MAKE) clean -e -C util_lbt_test + $(MAKE) clean -e -C util_tx_continuous + $(MAKE) clean -e -C util_spectral_scan + +### EOF @@ -0,0 +1 @@ +5.0.1 diff --git a/fpga/SX1301_FPGA_200K_NOTCH_LBT_SPECTRAL_SCAN_863_v33.hex b/fpga/SX1301_FPGA_200K_NOTCH_LBT_SPECTRAL_SCAN_863_v33.hex new file mode 100644 index 0000000..4cff280 --- /dev/null +++ b/fpga/SX1301_FPGA_200K_NOTCH_LBT_SPECTRAL_SCAN_863_v33.hex @@ -0,0 +1,1659 @@ +ff 00
+4c 61 74 74 69 63 65 00
+69 43 45 63 75 62 65 32 20 32 30 31 35 2e 30 34 2e 32 37 34 30 39 00
+50 61 72 74 3a 20 69 43 45 34 30 4c 50 31 4b 2d 43 4d 34 39 00
+44 61 74 65 3a 20 4f 63 74 20 31 37 20 32 30 31 36 20 31 31 3a 33 33 3a 31 32 00
+00 ff
+7e aa 99 7e
+51 00
+01 05
+92 00 20
+62 01 4b
+72 00 90
+82 00 00
+11 00
+01 01
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 01 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00
+00 00 00 00 00 04 00 00 00 00 00 00 10 00 00 00 00 00 00 40 00 00 00 00 10 00 00 00 00 00 00 40 00 00 00 00 00 01 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 04 00 00 00 00 00 00 10 00 00 00 00 00 00 40 00 00 00 00 10 00 00 00 00 00 00 40 00 00 00 00 08 03 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 50 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 03 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 30 00 10 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00
+00 00 00 00 00 0f 22 13 00 00 01 c0 1c 00 00 00 00 00 0f 70 01 00 00 07 00 c5 00 00 00 00 00 fa 21 30 00 00 00 21 78 f5 00
+00 00 00 00 00 0b 70 04 90 80 00 02 00 e0 a5 01 00 00 01 ad 82 00 00 00 40 c0 a0 00 00 00 00 e3 00 49 08 00 03 40 06 0a 00 10
+00 00 18 00 80 0f 22 13 00 00 00 40 0d e5 00 09 81 85 80 10 00 10 80 00 20 c0 50 01 83 89 00 32 21 30 00 81 2c 02 00 24 00
+00 00 00 00 07 00 50 04 90 00 00 00 24 10 50 00 00 00 00 03 82 00 00 00 00 c0 0a 00 00 00 78 07 00 49 40 04 01 40 04 91 80 00
+00 00 10 00 01 9d 22 13 00 00 00 03 a1 e5 00 00 00 00 08 d8 00 02 00 06 80 e5 00 01 00 00 00 9a 21 30 00 00 0c 01 48 05 20
+00 00 01 00 00 08 78 04 90 00 00 00 02 40 a0 00 00 00 00 0f 01 00 40 00 00 c0 a0 00 10 18 f0 03 00 49 40 00 00 03 83 00 50 00
+00 02 00 00 19 9d 22 13 00 00 00 03 15 80 50 00 00 08 00 30 00 00 00 00 1e c0 50 00 00 00 1a 5a 21 30 00 00 00 04 18 50 00
+00 00 00 00 0d 08 58 04 90 00 00 2c 00 30 05 00 40 00 50 0b 01 00 00 04 00 00 05 00 00 05 00 07 80 49 00 00 20 03 64 0a 01 00
+00 00 00 00 00 0f a2 13 00 40 02 00 24 00 00 00 00 05 0a 10 10 00 03 40 16 8f 50 00 00 00 00 d2 21 30 00 00 00 00 5c 50 00
+00 00 20 00 00 00 70 04 90 04 00 0c 02 40 a5 00 00 00 50 07 80 00 00 0c 00 d0 a0 00 00 00 70 03 00 49 00 00 20 00 02 05 00 00
+00 00 00 00 00 0d a2 13 00 00 00 00 17 80 50 00 00 00 01 f0 10 00 01 46 9e c0 50 00 00 01 00 7a 21 30 00 00 1e 20 0e 50 00
+00 00 00 00 00 00 50 04 90 00 00 00 00 30 05 00 00 00 10 af 80 00 00 00 00 20 05 00 00 00 b0 07 80 49 00 00 40 00 24 0a 00 00
+00 00 00 00 98 0d a2 13 00 00 00 40 07 aa 00 00 00 07 1b 98 00 00 16 00 01 c5 00 00 00 0d 80 ba 21 32 00 00 04 00 5c 50 00
+00 00 00 00 01 00 78 04 90 00 00 00 20 00 a0 00 80 00 08 a3 00 10 00 14 00 d0 a0 00 00 00 b0 03 00 49 00 00 00 00 03 05 00 00
+00 00 10 00 b8 0d a2 11 00 00 02 02 1d 80 50 03 00 0f 0a b8 10 00 01 62 8c a5 00 01 00 08 1a 72 21 30 01 00 00 00 0a 50 00
+00 00 01 00 01 80 58 04 90 00 00 1c 00 10 05 00 30 00 58 a7 80 00 00 00 00 10 50 00 10 00 50 07 00 49 00 10 00 00 24 0a 00 00
+00 00 00 00 70 01 61 7d 80 00 00 07 21 c0 50 00 00 08 0c 90 02 04 02 06 80 12 40 00 80 03 0a 1a 21 30 00 00 20 28 58 c0 d0
+40 00 00 00 00 89 04 17 d0 00 00 00 00 40 0a 00 00 00 90 03 80 00 28 14 03 4a 18 00 00 00 91 0f 00 49 90 04 00 44 03 83 04 04
+00 00 18 00 81 e1 e1 40 00 60 02 00 2e d5 60 01 80 07 00 10 01 00 00 03 96 cf 50 01 83 8d 80 12 21 30 0a 40 00 38 e8 96 80
+00 00 00 00 0f 19 08 14 00 00 00 1c 02 cf 20 00 00 00 08 03 00 00 00 00 41 d0 50 00 00 00 50 0b 00 49 00 a2 00 00 00 09 60 04
+00 00 30 00 00 09 62 80 08 c8 00 02 21 c0 12 20 08 01 80 f0 00 00 02 06 20 c5 00 01 28 00 1c 12 21 30 00 80 00 01 e8 96 80
+00 00 03 00 00 00 d4 28 40 04 00 00 00 40 02 00 01 20 08 0f 80 40 00 1c 41 50 a0 00 30 00 00 0f 80 49 40 04 00 00 00 09 60 10
+00 02 02 80 80 ad ba 04 00 00 00 60 2e a2 e0 00 00 09 80 30 00 00 60 e0 77 cf 50 00 00 11 81 1a 21 30 01 06 00 00 e8 96 80
+00 00 00 10 0f 00 9a 30 80 00 00 3c 03 d9 bd 00 00 00 98 0b 80 40 00 82 3e 50 50 00 01 06 08 9b 00 49 00 10 e0 00 02 09 60 10
+00 00 00 00 00 1d e4 00 00 02 20 00 7d a1 04 20 00 05 9b 18 00 00 00 06 a0 85 00 00 00 00 4c 5a 21 38 00 40 00 00 e8 96 80
+00 00 20 00 00 0d 96 c1 00 40 00 1c 21 40 30 40 00 28 00 af 02 00 00 00 39 50 a0 00 00 10 01 0f 80 49 00 08 c0 00 00 09 60 00
+00 00 00 00 00 03 a7 44 00 00 02 c3 2e d0 40 00 00 00 18 d0 00 00 00 00 05 a0 50 00 00 00 00 5a 21 30 00 00 16 05 c8 96 80
+00 00 00 03 80 08 1e 74 80 10 00 2c 42 cf 00 00 00 00 51 0f 02 00 00 1c 38 00 05 00 00 00 70 0b 00 49 00 00 01 c3 c2 09 60 00
+00 00 00 00 19 81 06 04 00 00 02 40 1f d8 00 00 00 07 89 38 10 00 00 00 74 ef 50 00 00 00 0c 7a 21 38 00 00 2e 69 c8 96 80
+00 00 00 00 00 00 93 79 c0 00 00 04 00 fc 00 00 00 18 09 c7 80 00 00 3c 35 50 a0 00 00 00 01 0f 80 49 00 00 01 c0 00 09 60 08
+00 00 30 00 10 ef b1 1c 0c c0 01 c0 3d 84 00 23 00 07 1b d8 00 14 e0 00 37 cf 50 03 00 00 00 72 21 10 0c 00 44 21 e8 96 80
+00 00 03 00 00 80 9c 01 40 0c 00 00 38 c0 d0 20 f0 00 08 a5 02 01 a0 00 03 40 50 00 10 00 00 f1 80 85 00 c0 08 e0 02 09 60 80
+00 00 00 00 81 87 f3 d5 00 02 02 02 0f ea 44 00 00 20 00 00 00 02 01 42 e4 98 00 00 00 08 00 f3 bd 40 00 00 be 01 e8 96 00
+00 00 00 00 01 08 9c 28 10 00 40 2c 2b 6f 80 40 00 44 00 00 00 00 00 36 61 5b 00 00 00 00 90 c9 41 40 00 00 02 60 02 09 60 50
+00 00 18 00 80 01 7a 81 00 60 01 e0 2c fa 14 00 00 00 00 00 00 00 00 07 a4 88 00 20 30 08 00 1e b4 40 80 0c 20 39 7c f5 00
+00 00 00 00 01 00 d0 3d 50 00 00 24 2e da 02 40 00 00 00 00 00 00 00 34 03 50 00 10 03 00 d0 c0 03 44 04 00 03 40 3f 05 00 80
+00 00 30 00 00 07 b3 d5 00 42 00 02 af ea 44 00 00 20 00 00 00 04 c3 e0 16 aa 00 00 00 00 00 7a 94 00 00 40 20 00 6c a0 00
+00 00 03 00 00 09 90 28 10 04 00 00 6d 7f 80 40 00 15 00 00 00 00 a0 0e 2c 10 a0 00 00 00 00 90 c1 40 02 02 06 c0 07 0f a0 00
+00 02 00 00 80 03 22 95 00 00 01 42 a6 e8 54 00 10 00 00 00 00 00 01 42 a1 ea 00 00 00 01 80 00 49 00 00 a0 00 6b ec 00 00
+00 00 00 00 09 00 bc 15 50 00 00 36 42 fc 40 40 00 80 00 00 00 00 00 36 6e 40 50 00 00 00 90 03 3c 60 00 04 03 c4 14 82 00 00
+00 00 00 00 19 b7 23 d5 00 00 00 40 2e ea 44 00 00 00 00 00 00 00 01 c0 0c aa 50 00 00 27 80 32 bd 40 00 04 20 6a eb e2 00
+00 00 20 00 00 08 9c 28 10 00 00 1e 03 7f 80 40 09 00 00 00 00 00 00 02 00 40 aa 01 00 00 00 01 42 80 00 00 00 c0 1e 88 12 00
+00 00 00 00 99 81 ba 81 00 00 00 06 24 e8 54 00 00 00 00 00 00 00 00 40 21 ea 00 00 00 07 00 97 34 40 80 00 00 38 00 00 00
+00 00 00 00 03 00 d0 3d 50 00 00 2c 32 db 40 40 00 00 00 00 00 00 00 02 02 50 50 00 00 00 58 af 81 40 00 00 00 06 00 00 00 00
+00 00 00 00 d8 97 73 d5 02 00 00 07 6e 8a 44 00 10 00 00 00 00 00 00 00 07 ca 00 00 00 00 00 3e bd 40 00 00 00 04 00 00 00
+00 00 00 00 05 1a 90 28 10 00 00 00 76 ff 80 40 00 85 00 00 00 00 00 00 00 20 a0 00 00 00 10 91 02 80 00 00 80 02 00 00 00 00
+00 00 30 00 d8 18 22 81 00 40 00 00 25 c0 50 00 00 00 00 00 00 00 01 42 86 aa 00 04 00 00 00 97 34 40 c0 00 04 04 00 00 00
+00 00 01 00 05 0a 54 14 10 04 00 1c 38 60 5a 00 00 00 00 00 00 00 00 00 01 40 f5 01 00 00 00 ff c1 40 00 00 00 03 40 00 00 00
+00 00 00 00 00 10 2a bd 02 00 20 43 85 dc 10 00 00 00 00 00 00 00 02 02 be 8b 7c 00 00 00 00 d3 28 10 00 0e 84 78 5e 83 40
+00 00 00 00 00 0f d0 17 d0 00 00 02 02 ed c7 00 00 00 00 00 00 00 00 04 22 68 12 42 00 00 00 03 03 d5 00 00 62 67 a4 ce 34 10
+00 00 18 00 d0 03 2c 08 00 00 00 00 20 ca 30 28 10 00 00 00 00 60 01 40 3c 99 5c 01 80 00 00 3a a8 10 02 00 00 00 6f f1 40
+00 00 00 00 0d 80 f6 06 40 00 00 00 26 c0 53 00 02 80 00 00 00 00 00 1c 28 4f 56 48 00 00 70 03 43 e9 00 a0 00 03 c6 e7 05 10
+00 00 18 00 81 e1 bb d4 00 00 00 00 1d ef 50 00 00 00 00 00 00 40 08 06 3e 9b 7c 00 00 48 18 53 28 04 00 00 0e 39 cc f5 40
+00 00 01 00 01 1b 34 01 40 00 00 00 02 e0 a0 00 00 00 00 00 00 04 01 40 03 e8 12 48 00 00 14 00 02 80 01 00 08 00 37 05 04 90
+00 02 00 00 00 03 04 00 00 00 00 00 1e c0 50 00 00 00 00 00 00 00 00 60 14 99 5c 00 10 09 98 ba bd 50 00 00 26 65 dc a5 40
+00 00 00 00 00 00 f2 10 80 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 02 6f 56 48 00 80 10 05 41 41 00 00 02 c3 46 05 54 00
+00 00 00 00 00 01 ba 80 00 0a 00 07 a0 e0 50 00 00 00 00 00 00 00 00 00 1e 8b 7c 10 00 09 80 fe bd 50 40 00 20 21 da f5 40
+80 00 20 80 09 09 04 28 00 02 00 34 6c c0 0a 00 00 00 00 00 00 02 00 1c 26 ec 12 40 08 00 30 05 01 41 00 00 00 40 3e 05 04 10
+00 00 00 00 80 00 30 14 00 00 e1 42 00 c0 50 00 10 00 00 00 00 04 01 46 76 99 5c 00 00 08 00 be bd 50 40 00 00 69 7c a5 40
+00 00 00 00 01 00 f0 02 82 00 00 26 78 40 0a 00 02 80 00 00 00 00 a0 14 20 cf 56 48 00 00 70 0f 01 41 00 00 01 c0 1c 05 54 00
+00 00 00 00 00 11 b8 7c 00 0a 00 00 1c c5 00 00 00 00 00 00 00 00 00 62 9e 9b 7c 00 00 00 00 ff bd 50 40 00 06 63 da f5 40
+40 00 00 00 00 0f d7 46 80 00 00 34 28 10 50 00 00 00 00 00 00 00 00 00 03 ed 12 40 40 00 00 0d 41 41 00 00 2b c3 57 05 04 00
+00 00 30 00 10 03 e2 04 08 00 03 e0 06 c0 50 00 10 00 00 00 00 c0 01 c2 ee 9b 74 01 00 08 01 b6 bd 50 4c 00 24 00 5b f1 40
+00 00 03 00 00 00 d0 38 80 00 00 3c 00 20 05 00 00 80 00 00 00 0c 00 00 3b cd 21 48 10 00 70 87 42 81 00 c0 03 c0 25 db 06 04
+00 00 c0 00 80 a7 65 d1 00 00 04 02 74 ca 00 00 00 80 00 00 00 00 00 00 5c 88 4c 00 00 0f 1a 56 bc 00 00 00 26 63 ca f4 40
+00 00 20 00 0f 0e 1f d4 14 30 00 04 68 e0 fa 08 00 00 00 00 00 00 00 00 3f c0 12 42 00 00 58 0d 82 94 00 00 02 c0 24 fd 04 04
+00 00 18 00 00 a3 6b 00 00 00 00 00 1c 8a 50 00 00 00 00 00 00 60 02 00 04 88 4e 00 28 08 00 1a a1 c0 8a 00 06 71 ca c3 61
+08 00 04 00 03 10 9f bf 00 00 40 00 27 d0 aa 00 80 00 00 00 00 00 00 1c 02 e0 12 40 11 00 70 00 02 1c 00 20 00 03 9e 8d 14 00
+00 00 10 00 51 c7 71 1d 0c 00 00 00 75 ca 00 00 03 80 00 00 00 c0 00 06 54 88 4c 08 00 25 0b 12 a1 c0 80 00 3c 01 ce c3 60
+00 00 01 01 00 1a 1c 10 98 20 00 00 2a f0 fa 08 00 18 00 00 00 04 00 00 23 e0 12 40 00 04 50 b0 02 1c 04 00 00 43 fd de 24 04
+00 01 00 00 18 a3 2a 80 00 00 00 00 2c 8a 50 00 00 00 00 00 00 00 08 66 54 88 4c 00 00 00 01 3a a8 00 00 80 00 01 ce e5 60
+00 00 00 00 00 00 7b 39 c0 00 00 00 00 f0 55 08 00 00 00 00 00 00 00 40 22 e0 12 40 00 00 d0 a0 02 80 00 14 00 00 26 e5 84 00
+00 00 00 00 30 15 3b 14 08 00 11 e6 9e 8f 50 00 00 20 00 00 00 00 00 03 de 88 4c 00 00 40 00 82 a8 00 00 24 0e 04 6b 83 40
+08 00 00 80 00 08 d8 32 40 02 00 80 26 50 a0 00 88 00 00 00 00 02 00 00 3b c0 12 40 4a 00 00 03 01 40 00 00 02 e2 1f cd 34 00
+00 00 00 00 00 e1 7b 89 4d 00 e1 66 86 8a 50 00 00 00 00 00 00 00 00 00 16 88 4c 00 00 00 00 3a a8 00 00 00 20 01 dd c3 41
+00 00 00 00 09 00 f4 20 52 30 00 24 26 f0 55 08 00 00 00 00 00 00 00 00 02 c0 12 40 00 00 f0 00 42 80 00 00 00 43 1f 8d 14 10
+00 00 00 00 80 07 3b d4 00 00 63 60 15 ef a0 00 00 20 00 00 20 00 02 03 96 88 4c 00 2b 03 c0 5a 7d c0 00 02 3e 73 ec a5 40
+08 00 00 00 01 09 b4 3f c0 00 00 14 24 30 fa 00 00 01 00 00 00 00 00 04 03 e0 12 40 00 00 08 01 ff fc 00 08 01 c6 26 05 54 00
+00 00 10 00 00 ed f1 41 08 00 02 00 44 8a 50 00 00 00 00 00 00 40 02 00 16 88 44 00 08 00 0e 3b a8 00 0c 00 1e 7e 5e a5 40
+00 00 01 00 87 00 70 04 10 20 00 14 25 d0 55 08 00 08 00 00 00 04 00 04 02 e0 12 49 40 00 01 00 02 80 00 c0 e0 26 3d 05 56 80
+00 00 00 00 d1 d1 21 55 02 00 00 00 2e ab 04 00 00 00 00 00 00 00 01 42 16 af 50 00 20 4f 80 13 e4 40 10 04 1c 00 d9 d5 00
+00 00 00 00 01 88 14 16 90 00 00 00 02 6e f1 40 00 00 00 00 00 80 20 02 6c 10 f5 00 00 02 f0 e7 63 8c 00 00 02 62 34 8d 00 00
+00 00 18 00 d0 17 73 d4 00 60 00 00 1d 8a 14 01 80 00 00 00 00 00 e1 42 a6 af 50 00 00 00 00 b3 7f 00 00 a0 3c 25 fc c1 02
+00 00 00 00 01 09 54 3f c0 00 00 00 30 da 5b 40 00 00 00 00 00 80 00 36 6c 70 ff 00 00 00 f0 03 6d e0 00 04 01 c7 8f 0e 60 00
+00 00 00 00 01 89 21 c0 08 00 03 e0 74 cf 70 00 00 00 00 00 00 00 00 02 d6 a5 f0 00 00 00 1c 36 a1 c0 80 00 06 28 da 91 00
+00 00 00 00 05 0a 14 14 00 00 00 06 20 ca ff 00 00 00 00 00 00 80 04 34 78 10 5f 00 02 05 70 00 82 1c 00 00 00 00 2c 9d 30 00
+00 02 00 00 18 00 2b 4d 08 00 00 00 2e ca 50 00 00 00 00 00 00 00 01 40 4e cf 50 20 03 a0 00 3a 21 c0 80 00 1d 29 40 e7 02
+00 00 00 00 00 00 94 38 d0 40 00 00 2f 4c f7 08 00 40 00 00 00 80 00 14 26 60 81 00 00 00 70 e0 c2 1c 00 00 01 74 04 08 10 00
+00 00 00 00 00 05 61 54 00 02 02 60 74 cf 52 00 00 00 00 00 08 00 02 63 2d af f2 20 00 00 1e 02 a1 c0 80 00 3c 3a f8 42 02
+00 00 20 00 00 00 08 15 40 00 40 04 38 c0 ff 08 00 00 00 00 00 00 00 04 41 70 fb 01 00 18 30 81 01 2c 00 00 a2 c2 a7 0d 10 10
+00 00 00 80 80 05 a3 c1 02 0a 00 03 54 c2 00 20 00 00 00 00 00 00 00 00 06 ef 70 00 00 47 00 02 a8 00 00 00 40 3e 1b c5 00
+00 00 00 10 05 0a 94 3d 50 00 40 24 70 10 20 08 00 00 00 00 00 40 00 00 24 4a ff 00 00 02 08 95 41 40 20 00 c0 07 94 9c a0 10
+00 00 00 00 50 0d 62 80 00 00 01 c0 75 a2 50 20 00 00 00 00 00 00 08 40 06 cf 70 20 01 21 01 00 00 00 01 00 00 38 dd 47 00
+00 00 00 00 00 00 b8 00 00 00 00 02 24 f0 2a 00 00 00 00 00 00 40 01 42 02 60 ff 10 00 04 08 b0 00 00 00 10 00 04 0f b4 20 10
+00 00 30 00 10 03 e3 88 08 c0 00 46 9f c5 b0 23 40 07 00 00 08 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 40 20 4f 74 00
+00 00 03 00 00 80 54 14 00 0c 00 02 43 f0 02 00 b4 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 2d da 00 10
+00 00 00 00 18 0d 2c 41 00 00 00 63 8d ca 00 00 00 00 00 00 00 00 01 43 1c 98 4c 02 00 00 0c 7a 3e 80 00 40 04 70 4e 85 00
+00 00 00 00 00 00 97 49 10 00 00 00 01 50 00 00 00 00 00 00 00 80 00 02 01 4b 12 42 00 00 00 00 c3 e8 40 0a 00 00 2c be a0 00
+00 00 18 00 00 15 f3 bc 00 60 03 40 07 a4 74 30 00 00 00 00 00 60 00 60 1e 88 4c 00 80 00 00 f7 be c0 80 00 20 06 6c c5 02
+00 00 00 00 00 0b d6 bf c0 00 00 04 00 40 81 68 80 00 00 00 00 40 00 00 00 4c 12 40 28 00 00 07 43 fc 20 00 00 c2 de 0c 60 00
+00 00 34 00 80 0f 29 f1 08 c4 00 e6 a4 c1 00 00 00 00 00 00 00 c0 01 43 54 98 4c 00 00 01 00 fb 3f c0 02 00 00 22 1c e2 02
+00 00 03 40 0d 0e 90 0f 10 04 20 02 02 ea 26 00 00 20 00 00 00 44 00 02 21 6e 12 40 00 00 08 0f 03 d4 20 00 00 00 0d 0d 10 00
+00 02 00 00 18 05 f3 fc 00 00 00 60 54 e5 b4 20 40 00 00 00 00 00 00 07 de 88 4c 00 00 00 01 87 bd 44 00 00 1e 05 6e 03 02
+40 00 00 00 00 00 56 fd c0 00 00 00 20 20 5b 40 44 00 00 00 00 80 00 00 20 4d 12 40 00 00 00 87 43 e8 00 00 00 03 ec 05 30 00
+00 00 00 00 10 01 01 f9 08 00 01 c0 21 ef a0 00 00 00 00 00 00 00 00 66 1e 9b 7c 00 40 03 80 da 00 00 20 01 40 3b cc a0 00
+00 00 20 00 00 00 00 1f 90 00 00 02 3a d0 f5 00 00 00 00 00 00 40 00 80 3b 4e de c0 04 00 08 0f c1 40 00 00 03 46 e7 0f a0 00
+00 00 00 00 80 10 3b e8 00 00 00 c6 40 af 70 00 00 00 00 00 00 04 02 06 1e 98 4c 00 28 50 00 00 3c 10 00 00 04 72 cd d5 00
+00 00 00 00 03 0a 30 3d 40 00 00 00 20 c9 fb 00 00 04 00 00 00 40 00 0c 00 48 12 40 01 00 10 e0 03 c3 80 00 03 44 0d bd 00 00
+00 00 00 00 d8 ed 2b dc 48 04 00 63 97 c5 05 28 00 00 00 00 00 00 02 00 16 98 4c 00 00 03 80 7f bd 48 00 21 14 02 4e c5 02
+00 00 00 00 07 40 b4 3f c2 00 20 00 01 e0 b8 48 00 00 00 00 00 40 00 04 01 6e 12 40 00 00 08 07 83 fc 80 00 03 63 9e 0c 90 00
+00 00 30 00 00 07 70 01 00 c0 00 e0 0e fa 50 00 00 00 00 00 00 c0 02 40 1e 9b 7c 03 00 08 1f 3f be c0 80 00 7e 00 7c c5 02
+00 00 01 00 00 00 56 c2 10 0c 00 00 3e 78 8b 00 00 00 00 00 00 44 00 06 02 4d de c0 10 00 f0 e9 c3 fc 20 00 01 40 00 0c 50 00
+40 70 00 00 81 87 f3 d5 00 00 00 e6 00 0a 54 00 00 09 0e 80 00 00 02 00 0e 8b 74 04 00 0b 00 00 19 9c 10 00 00 22 6a f5 40
+00 04 20 00 01 00 7c 28 10 00 00 00 3a c0 5a c0 00 00 f9 05 00 00 00 04 02 c8 de c4 00 00 18 0b 22 65 01 00 43 c3 ee 05 04 80
+00 00 18 00 80 09 3a 95 00 60 02 60 0c c0 00 11 80 00 00 00 00 60 00 00 00 c0 f0 01 80 08 00 77 00 54 12 00 00 3b 6e f5 40
+08 00 00 00 01 00 d0 15 50 00 00 3c 29 60 50 01 00 00 00 01 00 00 00 00 00 10 0f 00 00 00 30 0f b7 89 44 a0 00 03 9e 05 04 80
+6c 00 30 00 00 05 33 d5 00 00 03 e0 00 8f 04 00 00 00 00 80 00 c0 00 00 00 00 00 01 00 00 0f 33 82 9c 00 00 7e 62 6e f5 40
+03 80 03 00 05 0a 78 28 10 00 00 06 00 10 f0 c0 00 00 00 0f 00 0c 00 00 00 00 00 00 30 80 01 db c2 95 81 00 00 40 27 05 06 00
+00 03 00 00 00 bb 3a 95 00 00 00 07 c0 8f 04 00 04 0f 81 00 00 02 00 00 00 00 00 00 03 83 80 13 28 00 00 00 20 3a 4a f5 40
+00 00 00 00 05 0d 58 15 50 00 00 00 70 10 f0 c0 00 00 f8 f9 00 00 40 00 00 00 00 00 00 00 08 00 42 80 00 00 04 40 1d 05 04 08
+00 00 00 38 19 b7 a3 d5 00 00 12 00 47 f5 02 00 00 00 00 00 00 0a 00 00 40 00 00 00 40 05 8f b3 89 98 d0 0c 00 00 5a f5 40
+00 00 20 00 00 18 74 28 10 00 00 9c 21 5b b8 00 00 00 00 01 00 00 40 00 3c 00 00 00 04 00 00 83 c2 25 20 08 00 00 06 0a 04 80
+40 00 00 00 19 81 b8 15 00 00 00 00 27 8a f0 00 00 00 00 80 00 02 08 c0 00 00 00 00 00 0f 00 13 35 40 00 00 a6 02 4e a0 50
+00 80 00 00 00 00 00 01 50 00 00 00 03 50 ff 08 00 00 90 05 00 00 01 44 00 00 00 00 00 00 18 07 63 40 00 00 05 c3 cd 0f a4 08
+00 28 00 00 50 85 73 d5 00 00 22 00 00 e7 00 20 00 00 00 80 00 00 00 07 00 00 00 00 00 08 19 37 30 40 81 40 06 77 6e f5 00
+08 06 c0 12 00 10 70 28 18 00 00 1c 01 40 b0 08 00 00 00 07 00 00 00 00 00 00 00 00 00 00 50 e1 03 98 00 00 02 46 3c 0a 00 00
+40 00 30 00 10 15 ba 95 00 c0 00 40 66 c0 00 03 00 03 0c 80 00 c4 00 00 00 00 00 01 39 00 00 3e 09 40 8c 00 04 70 5c e2 02
+00 00 03 00 03 0b b8 2a 90 0c 00 00 33 48 10 20 10 00 09 0d 00 04 20 00 00 00 00 00 30 28 00 0d 03 ec 20 c8 84 c0 02 0e 20 08
+00 00
+11 01
+01 01
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 48 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 04 00 00 00 00 00 00 10 00 00 00 00 00 01 40 00 00 00 00 10 00 00 00 00 00 00 40 00 00 00 00 0d 11 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 00
+00 00 00 00 00 04 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 80 30 00 00 00 00 00 00 40 80 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 02 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 10 00 00 8f c1 41 00 c0 00 02 3f 05 04 00 00 00 00 00 00 40 00 02 3f 05 04 03 00 00 08 fc 14 10 04 00 00 23 f0 50 40
+00 00 03 00 01 81 f6 bd 50 0c 00 04 06 da f5 40 00 00 00 00 00 04 00 00 06 da f5 40 30 00 10 1b 6b d5 00 c0 00 07 ff af 54 00
+00 00 00 00 01 8f c3 d5 00 00 00 02 15 d7 24 00 00 00 00 00 00 00 00 02 15 d7 24 00 00 00 08 07 6c 50 00 00 00 21 70 50 40
+00 00 00 00 01 8f 7a a8 10 00 00 06 06 1e b1 40 02 00 00 00 00 00 00 06 06 1e b1 40 00 00 18 13 fa c5 00 00 00 60 6f ef 54 00
+00 00 00 00 10 0b 43 d5 00 00 00 00 16 05 04 00 00 00 00 00 00 00 00 00 16 05 04 00 00 00 00 5b 10 90 00 00 04 01 60 50 40
+00 00 00 00 00 00 7e 28 10 00 00 00 02 da f5 40 00 00 04 00 00 00 00 00 02 da f5 40 00 00 19 ab 07 9d 00 00 00 07 bf af 54 00
+00 02 00 00 01 0f 43 d5 00 00 00 02 b4 d7 24 00 00 00 00 00 00 00 00 02 f4 d7 24 00 00 00 0f d3 5c 90 00 00 00 03 60 50 40
+00 00 00 00 00 0e 5b a8 10 00 00 00 00 1a b1 40 00 00 00 00 00 00 00 00 00 1a b1 40 00 00 00 00 6a c5 00 00 00 00 0f af 54 00
+00 00 00 00 10 0b 83 d5 00 00 00 40 3f 05 04 00 28 00 00 00 00 00 00 40 3f 05 04 00 00 01 00 fc 14 10 00 00 04 03 f0 50 40
+00 00 20 00 0d 18 57 a8 10 00 00 26 60 ca f5 40 01 00 00 00 00 00 00 26 60 ca f5 40 00 00 81 83 2b d5 00 00 02 66 1e af 54 00
+00 00 00 00 10 a7 c1 41 00 00 00 40 1d d7 24 00 00 00 00 00 00 00 00 40 1d d7 24 00 00 01 00 77 5c 90 00 00 04 01 f0 50 40
+00 00 00 00 08 18 33 bd 50 00 00 20 60 0e b1 40 00 00 00 00 00 00 00 20 60 0e b1 40 00 00 81 80 3a c5 00 00 02 06 0e ef 54 00
+00 00 00 00 00 ab 03 d5 00 00 00 00 16 05 04 00 00 00 00 00 00 00 01 c4 16 05 04 00 00 00 00 58 14 10 00 00 00 01 60 50 40
+00 00 01 80 00 00 76 a8 10 06 00 00 68 ce f5 40 00 00 00 00 00 06 00 00 60 ce f5 40 1a 80 00 03 3b d5 00 60 00 02 9e ef 54 00
+00 00 00 00 00 0d 81 41 00 00 00 00 2d 0f 54 00 00 10 00 00 00 00 00 00 2d 0f 54 00 00 00 00 b4 3d 50 00 00 00 03 d0 f5 40
+00 00 00 00 00 00 32 bd 50 00 00 00 01 58 a0 40 00 00 00 00 00 00 00 00 01 58 a0 40 00 00 00 05 72 81 00 00 00 00 35 8a 04 00
+00 00 30 00 00 85 c1 41 00 40 00 02 3d 05 04 00 00 00 00 00 00 c0 00 02 3d 05 04 01 00 0f 08 f4 14 10 04 00 00 23 d0 50 40
+00 00 01 00 00 01 b6 bd 50 04 00 00 06 fa f5 40 00 38 00 00 00 0c 00 0e 07 5a f5 40 30 00 00 19 eb d5 00 40 00 40 7f af 54 00
+00 00 00 00 00 87 75 c9 00 00 00 02 15 c7 24 00 00 00 00 00 00 10 00 02 15 a7 24 00 00 00 08 54 14 10 00 00 00 21 50 50 40
+00 00 00 00 01 81 07 ac 50 00 00 06 06 1f b1 40 00 00 00 00 00 01 00 06 06 1e b1 40 00 00 18 1f fb d5 00 00 00 60 6d ef 54 00
+00 00 00 00 00 03 c3 e9 00 00 02 40 14 05 04 00 00 00 00 00 00 00 00 00 14 05 04 00 00 03 00 50 14 10 00 00 00 01 40 50 40
+00 00 00 00 01 80 b6 a8 10 00 00 00 02 fa f5 40 00 00 00 00 00 00 00 00 03 5a f5 40 00 00 80 09 eb d5 00 00 00 00 3f af 54 00
+00 02 00 00 00 e1 35 c9 00 00 00 00 34 c7 24 00 00 00 00 00 00 00 00 00 34 a7 24 00 00 00 00 d0 14 10 00 00 02 03 40 50 40
+00 00 00 00 00 01 06 ac 50 00 00 00 00 1b b1 40 00 00 00 00 00 00 00 00 00 1a b1 40 00 00 00 07 eb d5 00 00 01 e0 0d af 54 00
+00 00 00 00 18 e9 83 e9 00 00 00 40 3d 05 04 00 00 00 00 00 00 00 00 40 3d 05 04 00 00 01 00 f4 14 10 00 00 04 03 d0 50 40
+00 00 20 00 09 18 36 28 10 00 00 26 60 ea f5 40 00 00 00 00 00 00 00 26 61 4a f5 40 00 00 99 81 ab d5 00 00 02 66 1e af 54 00
+00 00 00 00 10 00 f6 c5 00 00 00 40 1d c7 24 00 00 00 00 00 00 00 00 60 1d a7 24 00 00 01 00 74 14 10 00 0a 04 01 d0 50 40
+00 00 00 00 08 00 13 ac 50 00 00 20 60 0f b1 40 04 10 00 00 00 00 00 3e 60 0e b1 40 00 00 81 87 bb d5 00 00 02 06 0c ef 54 00
+00 00 00 00 d0 05 81 41 00 00 02 44 14 05 04 00 00 00 00 00 00 00 00 04 14 05 04 00 00 0d 10 50 14 10 00 00 24 01 40 50 40
+00 00 01 80 00 00 33 bd 50 06 00 00 28 ee f5 40 00 00 00 00 00 06 00 00 29 4e f5 40 18 00 80 a1 bb d5 00 60 00 02 9e ef 54 00
+00 00 00 00 d0 0b 43 d5 00 00 00 00 0d 0f 54 00 04 00 00 00 00 00 00 00 15 0f 54 00 00 00 00 14 3d 50 00 00 00 01 d0 f5 40
+00 00 00 00 00 18 56 a8 10 00 00 00 03 58 a0 40 02 80 00 00 00 00 00 00 01 58 a0 40 00 00 00 0d 62 81 00 00 00 00 35 8a 04 00
+00 00 10 00 01 8f c1 41 00 c0 00 26 3f 05 04 00 00 00 00 00 00 c0 00 12 3d 05 04 03 00 00 88 fc 14 18 0c 00 00 23 f0 50 40
+00 00 03 00 00 0f fe bd 50 0c 00 04 27 fa f5 40 00 00 00 00 00 0c 00 1c 06 7a f5 40 30 00 10 1b eb d5 00 c0 01 c0 65 af 54 00
+00 00 00 00 00 85 7d c9 00 00 00 02 15 f7 24 00 00 00 00 00 00 00 00 02 15 87 24 00 00 00 08 54 14 10 00 00 00 21 59 72 40
+00 00 00 00 01 81 87 ec 50 00 00 06 06 1f b1 40 00 00 00 00 00 00 00 06 06 1f b1 40 00 00 18 1b 7b d5 00 00 00 60 61 eb 14 00
+00 00 00 00 98 05 81 41 00 00 00 00 16 05 04 00 00 00 00 00 00 00 00 00 14 05 04 00 00 00 0f 58 14 10 00 00 00 01 60 50 40
+00 00 00 00 05 80 fe bd 50 00 00 00 03 fa f5 40 00 38 00 00 00 00 00 00 02 7a f5 40 00 00 00 0b eb d5 00 00 00 00 25 af 54 00
+00 02 00 00 00 0d 3d c9 00 00 00 00 34 f7 24 00 20 00 00 00 00 00 00 00 34 87 24 00 00 00 80 d0 14 10 00 00 00 03 49 72 40
+00 00 00 00 00 00 06 ec 50 00 00 00 00 1b b1 40 00 00 00 00 00 00 00 00 00 1b b1 40 00 00 70 03 6b d5 00 00 00 00 01 ab 14 00
+00 00 00 00 10 0f c1 41 00 00 00 40 3f 05 04 00 00 00 00 00 00 00 00 40 3d 05 04 00 00 01 00 fc 14 10 00 00 04 03 f0 50 40
+00 00 20 00 09 98 7a bd 50 00 00 26 61 ea f5 40 00 00 00 00 00 00 00 26 60 6a f5 40 00 00 99 83 ab d5 00 00 02 66 04 af 54 00
+00 00 00 00 10 07 7d c9 00 00 00 40 1d f7 24 00 00 00 00 00 00 00 00 40 1d 87 24 00 00 01 00 74 14 10 00 00 04 01 d9 72 40
+00 00 00 00 08 18 03 ec 50 00 00 20 60 0f b1 40 00 00 00 00 00 00 0c 20 60 0f b1 40 00 00 81 83 3b d5 00 00 02 06 00 eb 14 00
+00 00 00 00 00 05 81 41 00 00 00 04 16 05 04 00 00 00 00 00 00 00 01 c4 14 05 04 00 00 00 00 58 14 10 00 00 00 01 60 50 40
+00 00 01 80 00 00 7b bd 50 06 00 00 29 ee f5 40 00 00 00 00 00 06 00 20 28 6e f5 40 18 00 01 a3 bb d5 00 60 00 02 84 ef 54 00
+00 00 00 00 00 0f 03 d5 00 00 00 00 3d 0f 54 00 00 00 00 00 00 00 00 10 05 0f 54 00 00 00 00 b8 28 10 00 00 00 22 50 f5 40
+00 00 00 00 00 00 d7 28 10 00 00 00 03 58 a0 40 00 10 00 00 00 00 00 00 03 58 a0 40 00 00 00 09 ab d5 00 00 00 00 15 8a 04 00
+00 00 30 00 50 8b 83 e9 00 c0 02 42 3d e7 24 00 00 00 00 00 00 44 00 43 3e 01 24 01 00 00 08 b8 3d 50 04 00 00 23 70 50 40
+00 00 01 00 08 01 9e 28 10 44 00 06 06 1b b1 40 00 00 00 00 00 04 80 20 03 68 84 40 30 00 00 15 e2 81 00 c0 00 00 2f af 54 00
+00 00 00 00 00 89 83 d5 00 00 00 02 05 05 04 00 00 00 00 00 00 00 00 40 2e 01 24 00 00 00 18 5c 14 10 00 00 00 21 5d 72 40
+00 00 00 00 01 9b ff a8 14 00 00 06 67 fe f5 40 00 00 00 00 00 00 00 26 03 68 84 c0 00 00 18 fb 7b d5 20 00 00 00 61 bb 14 00
+00 00 00 00 00 03 c3 e9 00 00 00 00 1d 0f 54 00 00 00 00 00 00 00 08 03 3c 01 24 00 00 0f 00 58 14 10 00 00 1c 39 60 50 40
+00 00 00 00 01 98 9b a8 10 00 00 00 03 ea a0 40 00 03 00 00 00 00 01 40 03 68 84 c0 00 00 00 0f 6b d5 00 00 06 00 6f af 54 00
+00 02 09 00 00 00 26 c5 00 20 00 00 1c 05 04 00 00 00 00 00 00 20 00 00 2c 01 24 10 80 00 10 d8 14 10 02 00 00 00 2d b1 40
+00 00 00 28 00 00 3e ec 50 00 00 00 03 fa f5 40 02 80 00 00 00 00 00 40 03 e8 84 c0 00 00 00 e3 6b d5 00 00 00 00 05 bb 14 00
+12 00 00 00 10 0f c1 41 00 00 00 40 3d e7 24 00 01 80 00 00 00 00 1c 02 3e 01 24 80 01 01 00 fc 14 10 00 00 04 23 e9 42 40
+00 00 00 00 09 00 1a bd 50 00 00 6e 60 0f b1 40 00 00 00 00 00 00 00 06 67 48 84 c0 00 00 99 87 2b d5 00 00 02 66 4e 0e 74 00
+80 00 00 00 10 07 65 c9 00 00 a0 00 1d 05 04 00 20 40 00 00 00 40 00 02 2e 01 24 00 00 01 00 7c 14 10 00 04 04 42 d0 a0 40
+40 00 00 00 08 18 03 ec 50 00 00 00 69 ee f5 40 00 28 00 00 00 04 00 00 67 48 84 c8 00 00 81 83 3b d5 00 00 02 07 34 ef 54 00
+80 00 00 00 31 c5 81 41 00 00 61 44 00 eb 14 00 00 00 00 00 00 00 01 c0 3c 01 24 00 00 03 00 58 14 10 00 00 02 00 40 5a 40
+00 00 01 80 00 0b 1b bd 50 06 00 20 28 4f b1 40 00 00 00 00 00 06 00 20 03 c8 84 c0 18 00 00 07 3b d5 00 60 00 40 20 0a 54 00
+10 02 00 00 00 09 43 d5 00 00 00 40 05 0f a4 00 04 00 00 00 00 10 00 03 2c 01 24 20 00 00 00 d8 14 10 00 00 00 03 50 f5 40
+00 00 04 00 00 00 d6 28 10 00 00 20 01 f8 a0 40 00 00 00 00 00 00 00 00 07 c8 84 c0 00 00 00 0b 2b d5 00 00 00 66 05 ea 04 00
+00 00 10 00 10 0d 03 9d 00 c0 00 00 02 07 80 80 00 00 00 00 00 00 00 00 27 af 70 82 00 05 00 33 ff c0 48 00 00 25 58 ff 08
+00 00 03 00 09 80 97 bb d0 8c 00 04 01 78 78 02 00 00 00 00 00 00 00 00 03 c9 e7 00 30 00 80 03 eb dc 02 c0 00 00 77 cf 70 00
+00 00 00 00 00 01 82 84 00 00 00 42 2e 09 c0 00 10 00 00 00 00 0a 00 20 27 0f f0 80 88 01 88 58 3f c2 02 00 02 22 d0 55 c8
+00 00 00 00 00 00 5a a0 40 80 00 26 24 ea 93 02 80 00 00 00 00 00 40 16 68 7c f7 02 01 00 90 1f 7b d4 00 0a 00 46 6f ea 56 00
+00 00 00 00 10 85 80 3c 00 00 00 00 05 00 a0 00 00 00 00 00 00 00 00 22 81 0f 50 00 00 05 00 58 3e b0 00 00 00 20 ef 58 40
+00 00 00 00 00 01 b6 3c 00 00 00 00 72 4c a0 00 00 00 00 00 00 00 00 0e 6c 6e f5 00 00 00 80 09 ea 81 00 00 00 60 74 ee 54 00
+00 02 08 00 f0 db 40 3c 40 20 00 02 2c 0f 00 80 00 00 00 00 00 20 14 04 3e df f0 10 80 00 00 76 29 90 00 00 00 23 e0 fa c0
+00 00 00 00 09 00 12 bc 00 00 00 00 04 6a 0f 00 00 00 00 00 00 00 00 8e 71 5b f7 00 00 00 58 05 ea 95 00 00 00 00 5f aa 04 00
+00 00 00 00 50 e3 6c 75 02 00 00 04 01 0f 50 00 00 00 00 00 00 20 03 e4 03 0f 50 00 81 01 19 1c 3e b0 02 00 04 03 eb b5 40
+00 00 20 00 00 00 7f 46 90 00 00 00 70 4c f5 00 00 00 00 00 00 00 00 14 28 7a f5 00 00 00 99 91 aa 81 20 00 03 60 1c ea 14 00
+00 00 34 00 10 ab 78 31 00 00 00 c0 1d f7 24 80 00 00 00 00 00 00 00 23 95 00 10 08 00 09 00 94 3d 70 00 00 04 28 30 55 c0
+00 00 01 40 08 0e fe d7 90 00 00 20 00 0e b1 40 00 00 00 00 00 00 00 14 2e e8 00 22 00 38 81 8f 3a 81 00 00 02 62 9d e5 54 00
+00 00 00 00 70 0d 60 31 00 00 00 44 1e 05 04 00 00 00 00 00 00 00 03 c6 ec 80 00 02 00 00 0a 1a 9e 90 08 06 00 01 4f d8 d8
+00 00 01 80 00 00 ff 17 90 06 00 3c 29 ce f5 40 00 00 00 00 00 00 00 14 60 4a 80 00 28 00 00 05 ba 85 00 a0 01 40 00 be 44 00
+00 00 00 00 50 03 00 00 00 00 00 c0 3d 0f 54 00 00 00 00 00 00 00 03 42 74 ef f0 80 00 09 00 72 d9 50 00 00 00 01 cd 70 40
+00 00 00 00 05 00 13 28 00 00 00 20 01 d8 a0 40 00 00 00 00 00 00 00 1c 64 d8 f7 00 00 00 98 07 2e 95 00 00 00 60 06 af 14 00
+00 00 00 00 00 00 00 00 00 40 00 40 26 81 e0 00 00 00 00 00 00 40 00 02 c0 00 00 02 00 05 00 04 39 32 08 00 00 00 d0 0a 00
+04 00 00 00 05 80 00 00 00 0c 00 26 00 ff d2 00 00 00 00 00 00 04 00 00 04 00 00 00 30 00 80 01 6b 91 08 c0 00 00 00 80 50 00
+80 00 00 00 50 80 00 00 00 10 00 04 26 a8 54 00 00 00 00 00 00 00 00 40 00 00 00 00 00 55 0e b3 bf c4 00 00 00 02 40 fa 00
+00 00 00 00 0d 80 00 00 00 01 00 06 7b 7f 47 40 00 00 00 00 00 00 00 20 00 00 00 00 00 02 d8 11 3b dc 00 00 00 00 00 cf 50 00
+08 00 00 00 00 a9 b3 30 00 00 00 c2 2f 02 e4 00 28 00 00 00 00 00 00 02 fc 82 14 00 00 00 00 0c 28 02 00 00 00 00 0c c0 00
+04 40 00 00 00 01 52 0c c0 00 00 20 77 58 25 43 01 00 00 00 00 00 00 00 06 49 84 40 00 00 00 05 a2 80 00 00 00 00 00 cc 00 00
+00 36 00 00 00 00 ba 58 02 00 00 02 67 00 30 00 00 00 00 00 00 00 00 00 34 dd e4 10 00 00 0a d2 46 00 21 00 04 00 c0 fa 00
+00 00 00 00 05 80 12 25 a0 00 00 00 65 7c 43 02 00 30 00 00 00 00 00 06 03 69 b7 c0 00 00 00 08 00 90 00 10 02 60 00 cf 50 00
+00 00 00 00 01 00 3e 58 00 00 00 02 a5 0a a0 00 00 00 00 00 00 00 00 44 16 91 24 00 00 00 08 d4 14 30 20 00 00 00 0c fc 00
+00 00 10 00 00 0a 72 25 a0 00 00 14 01 cc 33 22 00 00 00 00 00 00 00 20 20 e8 84 c0 00 00 01 98 2a 81 00 00 00 00 00 cf c0 00
+00 00 00 00 00 b0 b6 58 00 c0 00 00 1d d0 f0 00 00 00 00 00 00 40 01 c2 06 d1 24 10 00 00 00 9c 14 30 0c 40 00 00 c0 f5 00
+00 00 00 00 00 0a 52 65 a0 0c 00 00 2a 69 87 00 00 00 00 00 00 04 00 00 05 49 84 c0 00 00 01 85 bb d5 01 4a 00 07 00 cf a0 00
+80 00 00 00 00 00 22 58 00 00 03 63 dd 0f a4 00 00 00 00 00 00 10 02 40 16 c1 24 02 00 00 00 12 df 90 08 00 00 00 d0 55 c0
+00 00 00 00 00 00 7a 25 a0 06 00 14 07 78 a0 40 00 00 00 00 00 06 00 06 00 e8 84 c1 28 00 00 00 06 f5 00 a0 00 00 00 8a a4 00
+80 02 00 00 08 00 2e 58 00 00 00 06 96 8f e4 10 04 00 00 00 00 00 41 c3 c4 01 24 24 00 01 08 17 56 10 00 00 00 2e 4c c3 c0
+00 00 00 00 05 0a 70 65 a0 00 00 00 2a df fc 40 00 00 00 00 00 02 00 00 65 c8 84 c0 00 00 98 17 bb 95 00 00 00 00 0c 09 94 00
+00 00 10 00 10 07 02 20 00 c0 00 20 36 89 30 00 00 00 00 00 00 c0 00 07 4d f8 50 03 00 0f 80 d6 79 40 0c 00 00 00 cc d0 00
+00 00 03 00 00 0a de 2e c0 cc 00 04 00 dc 6c 00 00 00 00 00 00 0c 00 1c 26 fd a5 01 30 00 90 01 66 f0 00 c0 01 c0 35 ad 50 00
+00 00 00 00 00 00 43 84 00 0a 02 c2 75 c0 f0 00 00 00 00 00 00 00 20 00 00 0f 04 00 00 00 80 5e 7f c0 40 00 06 21 4a 98 10
+00 00 00 00 00 0e 5a 38 40 c0 0c 20 05 d8 69 00 00 00 00 00 00 00 00 00 00 18 f0 41 00 00 10 09 af dc 00 00 02 e0 3d ae 50 00
+00 00 00 00 90 90 43 84 00 00 01 e0 36 0c c0 00 00 00 00 00 00 00 02 c2 00 0f 06 18 00 00 09 04 3c c0 00 00 1c 03 7c d3 60
+00 00 00 00 01 9a 52 38 40 80 00 0e 01 4a 33 03 00 00 00 00 00 00 00 3e 04 18 f0 60 80 00 00 00 63 cc 08 00 02 60 26 98 34 00
+00 02 00 00 00 00 40 3c 00 00 22 c2 4d 05 f0 00 00 00 00 00 00 00 00 40 01 0f 04 00 00 00 09 9e bf c0 00 a1 00 34 59 95 40
+00 00 00 00 00 18 03 03 c0 00 00 00 06 4e 50 00 00 00 00 00 00 00 00 26 00 08 f0 40 00 00 00 01 ef ec 00 00 e8 60 16 ba 54 10
+00 00 00 00 18 01 79 11 01 00 00 00 1d 00 f0 00 00 00 00 00 00 00 20 00 01 0f 04 00 00 01 1a 06 79 40 00 00 06 23 5f 70 40
+00 00 20 00 0d 00 9e 6e d8 20 00 00 00 ca f0 00 00 00 00 00 00 00 00 00 78 1e f0 40 00 00 f9 95 a7 94 00 00 02 e0 26 af 14 04
+00 00 30 00 08 01 c1 dc 00 00 00 02 f4 c6 f0 00 00 00 00 00 00 00 00 00 00 0f 04 01 00 50 0b d0 36 c8 00 00 02 24 7c 43 50
+00 00 03 00 05 18 b7 11 00 80 00 16 68 0e 9f 00 00 00 00 00 00 00 20 00 00 0c f0 48 30 02 01 8d fb dc 08 00 00 e6 1c 9e 34 80
+00 00 00 00 18 85 c1 68 00 00 00 04 01 00 f4 00 00 00 00 00 00 00 00 40 00 0f 04 00 28 00 19 d2 40 00 0a 00 1c 3b fb e5 40
+00 00 01 80 0d 01 ff 3c 00 06 00 00 28 0a 0f 41 00 00 00 00 00 06 00 26 00 0c f0 40 19 00 00 b5 bc 40 00 a0 02 00 0c 98 55 08
+04 00 00 00 01 bd 80 3c 01 00 00 20 00 0f 04 00 00 00 00 00 00 00 00 00 34 05 a4 00 00 05 0b b6 e8 10 00 00 0c 62 68 f0 60
+00 00 00 00 00 0a 17 3c 00 08 00 14 00 1c f0 40 00 00 00 00 00 00 40 00 62 00 a5 40 00 00 00 0f ea 01 24 00 e2 07 9c ed 14 00
+00 00 10 00 00 8f 41 41 00 c0 02 63 dd be 50 90 00 00 00 00 00 c0 01 c0 1f 0a 04 00 0b 10 8b 30 36 40 28 00 80 22 c9 d1 00
+04 00 01 00 00 01 b6 bd 50 24 00 1c 03 cb a5 00 40 00 00 00 00 04 00 20 60 7e 05 60 01 02 10 01 33 04 08 c0 04 07 45 ba 10 00
+80 00 00 00 50 85 71 c9 00 10 00 00 01 0f 04 00 00 00 00 00 00 00 02 63 d5 c1 d0 00 00 00 0f 16 b8 00 00 00 00 03 c0 5a c0
+00 00 00 00 00 01 87 ac 50 01 00 06 00 1c f0 40 00 30 00 00 00 00 40 1c 3c da 25 01 00 00 00 11 af 54 00 00 31 e0 20 0a 54 00
+0c 30 00 00 00 05 01 41 00 00 00 02 14 ff f0 00 00 20 00 00 00 0a 02 40 0e 8a 10 00 02 07 9d d8 00 00 00 00 fc 22 40 a5 00
+04 40 00 00 00 00 b6 bd 50 00 00 00 27 df f7 28 00 01 00 00 00 00 48 14 03 eb e2 08 00 00 b0 bb f9 00 08 00 c8 60 5d c5 00 00
+00 02 04 00 f0 0d 31 c9 00 00 e8 42 1d 00 10 00 00 20 00 00 00 80 03 c2 41 00 f0 80 20 0b 0f b8 09 00 08 00 02 01 50 00 20
+80 00 00 40 00 00 06 ac 50 00 01 20 05 fa 00 03 00 00 00 00 00 0c 00 36 00 0e 0f 01 00 00 f8 f7 a9 40 08 c0 40 e7 a7 e0 50 98
+00 00 00 00 10 0f 41 41 00 00 0b e3 fc af 00 00 00 00 00 00 00 00 10 32 dc f2 40 00 00 80 9f b0 3e 80 00 01 00 43 c0 a0 00
+01 00 10 00 09 98 32 bd 50 00 00 7c 03 ed e0 00 00 00 00 00 00 00 00 9e 00 00 18 00 80 00 78 a0 73 d4 00 00 01 63 a7 a0 00 1c
+00 00 00 08 10 03 42 81 01 00 08 00 2c 05 a4 00 00 00 00 00 00 40 62 c0 3c 05 20 00 00 41 0a 0c 3d 40 40 00 00 01 50 f5 01
+c2 00 00 00 08 18 53 bd 50 00 00 00 62 00 a5 40 00 00 00 00 00 04 02 06 00 ea d1 03 00 00 80 07 33 d4 00 00 00 06 a1 ef a1 04
+98 00 00 00 30 00 43 c1 00 00 60 00 03 05 04 00 00 00 00 00 00 00 08 03 75 05 a0 00 08 00 00 b7 3f c0 08 00 2c 40 10 03 00
+00 00 05 80 08 00 02 3c 10 06 80 00 00 dc 50 68 00 00 00 00 00 06 06 04 02 ec ad 02 00 30 78 03 ef dc 00 a0 c0 43 01 c0 32 20
+12 02 00 00 f0 d1 03 d5 00 10 00 00 34 ed b4 08 04 00 00 00 00 00 14 03 dd 0f f0 00 00 85 00 3c 00 00 00 02 04 43 4b 20 00
+00 07 00 00 01 81 13 a8 10 01 00 00 00 00 e7 60 80 00 00 00 00 00 00 80 01 fa af 00 00 00 00 0b 71 40 00 80 02 07 8d d0 00 00
+00 00
+11 02
+01 01
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 08 00 00 00 00 00 00 20 00 00 00 00 08 00 00 00 00 00 00 20 00 00 00 00 00 00 80 00 00 00 00 00 02 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 08 00 00 00 00 00 00 20 00 00 00 00 08 00 00 00 00 00 00 20 00 00 00 00 00 00 80 00 00 00 00 00 03 00 00 00
+00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 a0 25 be 00 40 00 00 3f cf e7 80 00 00 00 00 00 00 00 c1 a9 e5 81 81 00 42 04 0a 72 1e 04 00 00 02 80 d8 00 e0 80
+00 00 00 02 00 97 44 3e 80 00 42 bc 1e 98 00 00 00 00 00 00 08 00 2a 6d 6d 00 c0 00 01 00 a0 40 22 c0 00 04 28 02 00 00 00 00
+00 00 00 82 03 a4 00 60 04 16 08 16 c3 00 80 00 00 00 00 00 00 00 02 9c 18 19 00 10 00 f5 38 00 20 30 00 00 00 00 39 ca 00
+00 00 00 08 00 fe 80 3d 00 48 40 80 00 08 00 00 00 00 00 00 04 00 00 14 2c 50 a0 01 20 0f a0 b8 04 00 00 00 00 00 00 90 40 00
+00 00 00 2a 50 00 60 00 00 41 00 1d d0 00 00 00 00 00 00 00 40 10 20 55 d5 81 00 08 00 09 1b de 00 00 00 02 bd 58 50 00 00
+00 00 00 02 5a 02 c0 00 00 02 00 06 6d 00 08 00 00 00 00 00 00 00 01 28 18 00 80 00 c0 00 0d 34 e0 00 00 00 01 41 c3 8c 00 00
+00 02 00 21 f5 68 60 00 00 00 10 ce 90 0f 00 80 00 00 00 00 00 10 11 dd b5 8c 00 00 00 ff 32 00 3a 00 01 02 2d 7e 00 a0 00
+01 00 00 02 0f 7f c0 2d 00 00 01 a2 fc 00 d0 10 00 00 00 00 00 00 02 14 3f 00 00 00 00 4a f0 2c 04 10 00 20 02 80 d0 10 00 00
+00 00 00 68 05 be 00 00 00 10 33 c5 a1 01 00 00 00 00 00 00 20 00 30 9f e8 00 00 00 90 05 70 60 00 00 00 10 29 4c 38 00 00
+00 00 20 02 00 b2 cc 00 00 00 03 fc 2e 00 a0 00 00 00 00 00 00 00 03 01 ec d0 e0 00 00 00 a0 34 43 c0 00 00 02 81 00 0b 00 00
+00 00 09 0e f5 a2 00 00 00 00 10 ce e8 0e 00 80 00 00 00 00 00 10 00 07 f3 9c 00 02 04 af 3e a4 00 00 00 00 00 00 00 00 00
+00 00 00 00 ff 7f cc 00 02 00 02 4e ee b0 00 10 00 00 00 00 00 00 01 28 3c 08 00 00 00 0a f0 80 e0 00 00 00 00 00 00 00 10 00
+00 00 00 00 00 02 00 68 00 10 18 dd 91 0f 00 00 00 00 00 00 00 00 3b cf d7 08 00 00 00 04 93 c0 00 00 00 02 bd 48 00 00 00
+00 00 00 00 00 00 0c 31 00 00 02 8c 1d 18 d0 14 00 00 00 00 00 04 02 fd d8 08 00 00 28 00 05 b4 02 40 00 00 01 42 c0 00 00 00
+00 00 08 21 75 80 00 00 02 30 39 ce e8 00 00 00 00 00 00 28 10 00 00 00 03 88 00 0c 40 ef 7b 40 20 00 05 02 2d 5e 00 e0 02
+00 00 00 03 2b fb 80 00 40 20 03 7c 39 b0 01 00 00 00 00 00 02 00 00 00 00 f9 90 00 c0 0f f0 74 00 00 00 00 02 82 d0 00 00 20
+00 00 10 ac 75 a4 02 c0 00 00 5a 45 80 00 00 80 00 00 00 00 00 80 3b f5 a0 00 00 00 00 47 76 c0 06 01 00 0a 94 ea 81 8a 04
+00 00 00 1a c2 12 00 60 00 09 01 a4 10 00 00 84 00 00 00 00 00 00 23 ff 48 90 e0 00 00 08 11 a4 03 40 20 00 a9 a6 fd 0f 40 80
+00 00 10 28 51 bc 02 c0 55 00 48 44 c5 00 00 00 00 00 00 00 50 02 3f c0 08 00 01 58 40 47 58 2c 20 30 00 09 1d e9 80 18 01
+40 00 00 9a ad 12 80 7c 02 40 11 20 0d 08 a0 04 00 00 00 00 00 00 13 fc 00 10 00 00 08 08 b0 3b c7 00 02 04 b0 c7 d1 0a 40 10
+00 00 00 aa 51 bd 42 80 00 00 48 45 f7 8a 00 00 00 00 00 00 00 10 b0 c0 00 00 00 00 00 af 56 5e 05 00 00 0a 95 4e 10 10 00
+00 00 00 82 95 56 02 40 00 02 01 20 08 09 00 00 00 00 00 00 00 00 6b 0c 00 00 a0 00 00 00 50 60 23 08 00 00 aa 83 f1 88 1c 00
+00 02 00 6a 71 bc 67 80 00 00 5a 45 e0 00 00 00 00 00 00 00 00 00 00 16 97 00 4c 00 08 0a 38 60 60 05 80 0a bd 68 11 b4 00
+00 00 00 06 25 16 34 2c 00 02 01 a4 10 00 b0 00 00 00 00 00 30 00 02 80 2a 00 98 00 00 00 50 ab c3 90 28 04 82 80 f0 88 00 00
+00 00 00 20 57 f0 00 40 00 00 48 45 a3 9f 80 00 00 00 00 00 20 90 00 15 e8 08 80 20 04 0a 56 80 02 00 00 0a 95 5b 00 00 00
+00 00 20 03 5f 06 00 30 70 00 01 20 0b 08 d4 00 00 00 00 00 05 08 00 04 1d 71 00 02 00 00 a0 00 c0 00 00 00 aa 82 f0 0e 00 00
+00 00 11 2a 71 ac e6 00 40 00 48 44 a0 00 b4 80 00 00 00 00 00 05 00 06 80 00 14 00 00 af 53 c0 79 00 00 0a bc 69 10 08 08
+00 00 00 02 1a 17 00 00 00 00 01 20 0c 00 05 10 00 00 00 00 50 00 00 10 ea 00 90 00 00 00 50 a3 c0 08 00 02 82 80 a0 89 4d 00
+00 00 08 6a 55 fe 66 00 00 00 08 45 e0 1e 00 00 00 00 00 00 00 80 00 17 f0 00 00 80 10 a0 5f 20 28 00 02 0a 04 1f 90 00 00
+00 00 00 92 95 5e 04 00 00 08 01 20 1e 00 00 04 00 00 00 00 00 00 12 80 1b 00 00 00 00 0a 00 00 46 00 00 00 b6 fc 91 80 00 40
+00 00 10 aa 71 ee 00 40 22 00 00 00 01 00 50 00 00 00 00 11 00 52 00 04 80 00 00 0c 80 9d fb 5c 00 00 00 02 ad 5b f0 04 00
+c0 00 00 1a 25 16 c4 20 41 b0 00 00 00 78 e2 08 00 00 00 02 00 0a 00 04 2a 90 00 00 80 0a dd e0 00 00 00 20 02 d7 f1 8f bc 08
+00 00 01 25 53 00 00 40 00 00 03 2e a0 01 00 00 00 00 00 02 00 40 91 dd 90 19 00 00 0e 8d b0 40 70 00 00 09 1d fa 18 10 00
+00 00 00 03 aa 02 00 20 00 00 41 39 78 10 e0 00 00 00 00 00 38 00 0b 0c 69 00 80 00 20 24 e1 ae 46 01 00 80 b0 c4 e0 08 80 00
+00 00 01 25 a0 00 00 0a 05 00 a2 f4 00 0a 00 14 00 00 00 00 00 40 b1 ce a7 8e 00 54 00 f0 18 06 38 60 48 09 1d ef 00 10 01
+40 00 00 03 a5 03 1c 00 61 50 69 1c 6f 00 b0 01 00 00 00 00 00 00 0b 08 4f 78 a0 09 00 8f 00 03 c7 c0 04 00 b0 cc a9 08 00 10
+00 00 00 00 0b 68 00 00 00 00 01 fe b1 88 00 08 00 00 00 00 00 01 89 47 b1 0e 20 80 00 af 3a 00 00 80 00 1a 3d db 00 81 12
+00 00 00 80 02 92 00 00 00 00 00 3d 58 d9 00 00 80 00 00 00 82 00 0a 9d 6c 00 00 10 08 00 a0 24 00 10 00 20 83 9e d0 00 00 80
+00 02 01 0a f1 a0 00 04 20 10 10 9f d0 08 01 00 00 00 00 02 00 00 01 5c f0 09 10 40 00 00 00 16 72 00 00 09 1c 6f 18 01 14
+00 00 00 00 af 08 1c 28 00 00 00 28 1d 01 a0 00 00 00 00 00 00 00 02 bc 1e 01 f0 00 00 00 00 02 40 11 20 20 b0 e6 a0 08 10 00
+00 00 00 00 55 80 06 00 00 15 a1 56 e7 01 00 00 00 00 00 00 00 30 22 ce c0 0b 00 00 00 af 76 5c 04 00 04 19 1e da 79 90 00
+00 00 20 00 0a 0a 00 30 00 0c 7b 7c 1a 58 c0 00 00 00 00 00 00 00 03 6c 2b f1 c0 00 00 00 a0 31 63 40 20 00 b0 c6 e0 8c 00 00
+00 00 00 0f 51 a0 e3 80 20 00 11 a7 a1 80 00 00 00 00 00 00 20 40 ab df 97 80 00 00 40 ab 1b 56 78 77 00 48 97 4f 11 8a 08
+00 00 00 00 f5 08 02 70 05 00 03 09 cb 10 e0 00 00 00 00 00 01 00 08 28 09 00 f0 00 00 00 b0 f4 06 68 30 01 96 d4 c9 8c 15 40
+00 00 00 05 f5 80 03 80 00 10 02 8e d0 0e 00 00 00 00 00 00 00 00 01 57 ef 98 00 00 c0 83 5e 00 04 05 02 0a 3d fb 80 80 00
+00 00 00 00 af 0a 00 40 00 00 00 04 3d 10 00 00 00 00 00 00 00 00 00 14 00 f0 00 40 00 06 70 f4 03 80 00 00 83 9e f1 30 10 00
+00 00 08 0a bb 7c 06 84 03 34 82 9c c0 19 40 0c 00 00 00 00 50 10 22 cc a0 18 00 88 00 83 b7 c0 72 85 20 0a bd 7f f1 f0 00
+c0 00 00 00 0b 33 40 40 00 22 58 74 1f 00 8a 00 80 00 00 00 02 00 01 08 3c 00 c0 00 c0 00 1f 23 c4 10 23 00 81 42 d1 1e 00 08
+08 00 00 80 55 3a 00 00 00 00 28 1d 99 00 00 00 00 00 00 02 20 00 00 15 d0 01 00 00 00 e7 1a 60 6c 82 00 01 1d dc 80 00 01
+00 00 00 00 af 0a 54 00 00 00 00 00 1c 10 e0 00 00 00 00 00 00 00 60 10 d9 00 80 00 20 0e f7 be c6 c0 50 00 20 66 b5 0c 00 00
+00 00 00 04 29 a5 c0 40 00 10 38 06 95 00 08 10 00 00 00 00 20 87 2b c6 c8 00 1c 01 00 a0 76 76 78 00 01 01 1d 61 00 0a 02
+00 00 00 00 84 97 80 28 02 00 01 00 3c 30 a0 05 00 00 00 00 01 00 02 bc 20 70 00 00 28 00 00 67 44 00 50 80 22 c0 f0 00 00 00
+00 00 04 04 a3 e8 07 00 40 15 a1 57 f0 00 00 0c 00 00 00 00 00 91 03 0c b1 00 20 00 40 89 76 e0 00 01 00 02 bd 4e 71 c0 00
+00 00 00 00 a5 02 9c 40 00 08 4b bc 0b 00 00 00 c0 00 00 00 40 02 00 70 0e 08 f1 00 00 04 a0 f0 c3 80 00 00 01 42 a7 90 00 00
+00 02 00 8a 01 70 00 42 00 01 81 5f b1 1f 40 80 00 00 00 00 00 10 09 cf a9 8a ac 00 40 05 13 20 00 00 00 10 92 00 01 90 04
+00 00 00 00 a0 08 00 20 20 00 69 7c 1d 00 d0 00 00 00 00 00 50 00 00 9c 00 51 04 08 00 0e 70 ad c0 03 00 00 06 0c e5 0f 00 80
+00 00 04 04 73 e4 00 00 40 05 a1 5e 90 01 00 00 00 00 00 28 00 10 01 5f e8 0e 40 00 40 83 77 20 78 00 00 10 29 5c 00 12 00
+00 00 20 00 05 0e 1c 00 00 02 4b bc fb 00 d0 00 00 00 00 00 00 00 03 6c 28 10 fa 01 00 06 70 75 47 c6 00 00 02 82 0b 0d 00 00
+00 00 00 03 09 a6 67 c0 50 00 06 87 df 99 00 00 00 00 00 08 00 10 22 57 f3 0b 01 c1 40 07 3a 6c 04 00 00 02 bc e9 01 d8 00
+00 00 00 00 50 97 8e 64 00 00 02 75 c9 f8 80 00 00 00 00 01 00 00 03 1c 3e 09 e0 08 00 00 10 a0 63 87 00 00 02 80 b0 1d 40 00
+00 00 04 08 03 f0 00 06 00 00 04 66 f9 00 00 00 00 00 00 00 00 10 00 d6 00 00 20 01 00 cf 1b 00 69 00 04 01 01 60 80 10 00
+00 00 00 00 d0 07 40 34 00 00 00 0b fc 10 80 00 00 00 00 00 02 00 10 0c 20 00 04 00 20 0e fb 38 42 a8 00 80 20 2c c1 0f 00 00
+00 00 08 00 0b a4 00 42 00 12 00 c7 c9 08 01 48 00 00 00 00 10 00 2b d4 d7 8f 01 00 04 05 30 06 00 01 0a 00 29 48 00 00 00
+00 00 00 1c 02 9f 80 24 00 00 03 cc 1e 50 80 08 c0 00 00 00 06 00 00 28 3c b1 f0 08 10 00 a0 28 00 01 20 40 02 82 09 00 01 40
+00 00 00 a4 73 62 07 46 00 40 a3 d5 f8 18 01 c0 00 00 00 00 00 00 21 6d 8d 81 01 40 00 07 b2 9e 2c 00 04 0b 1d fb 19 e0 10
+00 00 00 02 c3 3f 44 3c 00 00 48 38 ce b0 f0 04 00 00 00 00 68 00 03 ab fe 78 f0 00 10 00 1d a3 62 42 00 00 b0 8d d0 1c 18 80
+00 00 00 a4 77 6b e0 50 04 00 91 d4 d7 99 40 90 00 00 00 01 00 00 23 ed 87 9b 18 00 00 47 b0 60 71 10 00 0a bd dd 71 e4 03
+00 00 00 02 c3 df ee a2 b0 40 2f 0c 4e 08 88 05 00 00 00 02 00 00 01 14 4d 31 e0 00 00 08 b5 23 c3 a8 00 00 81 41 d0 9f 20 14
+00 00 00 2a 5b 20 67 00 00 00 a3 dd e1 89 20 00 00 00 00 20 00 00 20 44 c7 8e 10 00 40 42 32 60 04 00 08 0a bd d8 f9 ef 48
+00 00 00 1a 59 72 4c 40 00 00 28 38 ef 01 8a 00 00 00 00 00 00 00 01 7e 7c 01 00 00 00 08 b0 6a 43 c0 00 00 82 81 e3 80 81 40
+00 02 00 68 f5 69 e6 17 50 02 a3 f4 d1 80 00 00 00 00 00 02 20 14 20 ce a0 01 80 01 40 81 1a 16 78 00 00 1a 95 5c 01 d4 00
+01 80 00 02 0e fe 02 00 82 00 0c 39 ec 00 00 00 00 00 00 00 41 00 01 9c 3d 00 f0 00 00 00 a0 37 43 c1 00 00 95 43 95 18 90 00
+00 00 08 6c 7b b2 c7 c0 00 04 91 ef 80 09 00 00 00 00 00 00 00 00 28 4c ef 00 40 00 00 05 18 0c 24 80 02 0a bd 69 00 18 00
+00 00 20 02 c2 13 9c 20 60 82 0b 0c 59 00 e0 00 00 00 00 00 04 00 01 5d 7c f0 ca c0 00 00 a0 24 07 d0 20 00 82 80 d0 08 40 00
+00 00 08 28 f5 ae 07 c0 50 44 91 f5 f0 08 10 80 00 00 00 10 20 10 20 cc e8 01 00 00 40 8b 3e 20 04 41 00 02 87 68 11 e0 00
+00 00 00 02 0d f2 44 7e 71 00 0b 0c 59 00 f0 00 00 00 00 00 05 00 01 9c 3d d0 f0 00 00 04 20 e3 c3 ce 20 00 15 cc b1 00 00 00
+00 00 00 2c 79 bf c3 00 20 00 a9 5d f9 00 0c 80 00 00 00 03 20 00 29 4d b7 18 00 80 00 47 da 40 00 62 01 02 0d 69 01 90 08
+00 00 00 82 c2 be 5c 00 04 00 1a a8 1e 18 80 10 00 00 00 00 04 00 41 54 29 f8 00 04 00 05 f3 a4 00 07 40 00 19 c1 a0 0d 00 00
+00 00 00 28 df 80 67 80 03 20 a3 f6 cf 88 00 0c 00 00 00 00 00 00 39 4e 97 80 21 40 40 05 7e 94 00 07 01 00 1d ff 58 c1 80
+80 00 00 8a 4e fb 7c 40 00 20 48 39 ec 19 c8 00 80 00 00 00 00 00 11 16 4a 08 00 40 00 0e 70 ac 20 00 30 00 2a c2 80 1e 10 08
+00 00 00 0a b3 34 06 28 00 40 b1 c6 e1 01 00 00 00 00 00 00 00 81 00 24 81 0a 18 80 0a c7 1a 7c 02 84 00 00 14 ce 51 c0 80
+00 00 00 00 0b d7 b4 35 00 00 4b 08 7c 00 80 c0 00 00 00 00 00 00 00 11 6e 09 00 10 00 2c 13 6c 43 d0 00 00 17 c0 80 8a 00 80
+00 00 04 08 35 a0 00 10 20 00 91 cd e3 80 00 94 00 00 00 00 00 80 39 e0 18 20 80 02 02 a5 da 00 24 80 55 03 6c f8 81 c8 04
+00 00 00 00 67 0f 40 3c 80 00 4b 0c df 00 00 01 40 00 00 00 00 00 03 6d cf f0 04 00 00 25 91 b0 06 10 05 00 22 c2 ab 1f 00 80
+00 00 00 0a b3 35 62 a2 00 00 91 c4 f1 bc 00 00 00 00 00 06 50 94 01 16 80 1f 00 10 02 47 36 66 00 20 00 10 14 4b 00 e0 00
+00 00 00 00 0b 16 80 01 00 02 0b 0e ef 00 81 c0 00 00 00 00 02 08 00 10 20 01 c0 00 00 ac 39 af c0 04 00 00 22 d6 80 0f 18 00
+00 02 04 08 33 2b e3 00 00 04 a3 d5 f1 99 50 80 00 00 00 00 00 40 01 55 b8 1c 8c 00 02 8f d6 ce 00 02 00 40 29 61 70 8a 1c
+00 00 00 00 67 03 56 00 02 00 08 3b fb 90 80 00 00 00 00 02 00 00 01 7c 0c f1 00 00 08 20 e1 e8 03 a8 40 00 01 40 80 00 20 00
+00 00 04 0a 37 a0 e7 40 00 24 a3 d7 98 18 00 00 00 00 00 00 00 80 28 0d f0 08 04 00 02 a5 d6 dc 64 00 00 02 95 4d 59 e0 00
+00 00 20 00 a3 00 02 70 00 00 08 39 5c 10 90 00 00 00 00 00 00 00 41 68 1f 01 00 00 00 25 9d a0 02 28 00 00 15 41 90 80 00 00
+00 00 05 0c 03 e4 00 68 50 01 a3 f6 b8 00 08 00 00 00 00 02 00 00 24 3f b0 1c 1c 00 00 05 56 44 05 71 03 02 2d c0 80 00 16
+00 00 00 00 ca 02 c0 38 01 00 18 3a 4b 70 81 80 00 00 00 00 05 00 01 41 ef 01 00 00 08 0a f0 b4 03 60 20 00 11 c2 ef 00 00 00
+00 00 10 02 75 81 c0 07 00 00 ab df b0 0e 00 00 00 00 00 07 00 10 30 0d f0 18 00 00 00 05 37 00 20 00 00 02 95 49 78 0a 80
+00 00 00 00 1b fa 40 00 00 00 08 14 3d 90 f0 00 00 00 00 00 00 02 03 28 0a 00 00 00 00 00 50 40 04 00 00 00 15 40 b0 08 00 00
+00 00 00 00 00 00 03 b0 20 01 a3 f6 af 1c 00 0c 00 00 00 00 00 80 01 1f 8d 80 80 82 00 05 30 40 00 50 31 02 2d e0 01 a0 00
+00 00 00 00 00 00 00 3e b0 00 08 3a 6a 19 00 00 c0 00 00 00 00 00 10 00 fa 38 81 04 00 00 a0 34 00 02 02 00 11 c0 e0 10 a0 00
+00 00 00 2e 37 e4 e6 40 00 00 22 5d e7 01 00 00 00 00 00 10 00 40 11 1e d7 1f 18 01 00 01 3a dc 00 00 00 49 1d 7e 81 91 c0
+00 00 00 0a 43 16 ae a0 80 80 03 2c fb 08 80 00 00 00 00 02 ec 00 01 20 4c 01 c0 00 00 08 37 a8 00 30 00 40 b0 c6 c1 08 14 00
+00 00 00 2c 35 77 e0 00 75 00 29 e7 d0 00 00 80 00 00 00 00 40 00 28 0e 10 00 80 80 00 af 56 40 24 04 04 0b 1c d9 00 14 01
+40 00 00 82 8b 12 46 bd 05 40 00 67 ce 90 90 00 00 00 00 00 02 00 01 40 0e 00 00 d0 00 00 50 a8 07 01 20 00 b0 45 e9 08 21 14
+00 00 00 22 ff 61 c7 05 00 10 28 df b3 8a 00 00 00 00 00 00 00 14 30 44 c7 00 00 00 40 01 56 ae 24 40 00 0b 1c ff 01 80 00
+00 00 00 02 0b 3e c0 00 00 00 02 4c 3d 10 0a 00 00 00 00 00 00 00 02 00 28 00 d0 00 00 0a 70 3a c2 80 00 00 b0 a6 90 00 00 00
+00 02 00 ec 3f f6 03 80 00 10 31 4c 80 0f 00 00 00 00 00 0a 00 00 01 4e 80 00 00 80 40 c5 5f 86 76 07 00 00 14 4b 01 8a 00
+00 00 00 02 8b d2 7c 02 00 00 03 14 20 f1 d0 00 00 00 00 00 00 00 01 7c 28 00 09 50 00 0c 50 40 03 90 30 00 2b c2 c1 00 40 00
+00 00 00 2c 33 a4 e0 00 00 14 30 16 cb 80 00 00 00 00 00 00 00 02 29 7e 89 08 00 00 40 ab 53 b6 00 00 00 0a 97 ed 00 80 08
+00 00 22 82 47 77 7c 3c 70 00 03 14 1e 78 e0 14 00 00 00 00 04 00 00 84 d8 78 c0 00 00 00 70 aa c0 00 08 80 96 7c 80 08 00 00
+00 00 01 20 d9 74 66 f8 20 00 22 cf 88 01 08 00 00 00 00 20 20 00 01 4c 80 01 00 00 40 89 5a 84 00 01 00 0a 3f 7f b8 e0 00
+00 00 00 02 4f 9b 3c 7d 80 02 02 2e f0 50 d0 00 00 00 00 01 00 00 01 7c 2d f2 b0 40 00 0c b0 a4 20 08 20 01 83 6c e1 9d 18 00
+00 00 00 2c 3b 66 02 00 00 80 19 d5 f5 88 1c 00 00 00 00 00 00 30 36 d4 c0 00 10 c0 40 47 7e 00 78 00 00 0b 1e fe 71 c0 08
+00 00 00 02 47 9f 04 40 00 00 01 0f fa 19 00 00 00 00 00 00 00 00 02 2c 0b 02 00 14 00 04 70 80 02 44 00 80 b0 ae f0 10 01 00
+00 00 00 20 55 b4 03 c0 02 00 23 7c a0 00 01 40 00 00 00 00 30 00 2b de c7 00 20 00 00 af 33 b6 00 00 00 40 9e e1 38 90 00
+c0 00 00 06 5f 07 40 3c 01 20 41 37 e9 10 82 08 00 00 00 00 00 00 00 14 29 08 8c 00 00 00 a0 b5 c0 00 00 00 06 ce f3 8d 00 08
+00 00 00 a4 71 fc 07 47 00 00 31 ce e8 01 00 00 00 00 00 00 70 00 2b 44 95 98 00 80 02 04 9f c0 04 00 10 02 84 cf 18 e1 88
+00 60 00 02 c3 17 00 20 00 00 01 1b ce f0 80 c2 00 00 00 00 03 00 02 24 6f 08 d0 14 00 21 c3 e0 02 00 00 00 2a e4 87 08 00 00
+00 00 00 aa f7 3e c3 40 05 00 20 4c e0 01 00 00 00 00 00 00 00 00 20 4c 90 00 00 00 02 80 ba 4e 04 05 51 02 25 f8 80 19 40
+00 00 00 02 0a 06 3e 30 00 40 02 be de 00 d8 00 00 00 00 02 01 00 01 7c ea 00 00 00 00 2e 07 3d 42 00 05 00 31 c1 cf 08 40 00
+00 00 00 20 51 b8 00 00 00 10 20 dc f3 80 00 80 00 00 00 00 00 00 39 74 c3 80 04 00 02 04 f7 a0 60 75 01 00 14 4a 38 10 00
+00 00 00 02 5f 07 40 00 00 00 22 6c 2c 08 00 00 00 00 00 00 00 00 01 15 df b0 d0 00 00 21 c3 e9 40 08 20 00 10 82 80 0d 00 00
+00 02 00 6a f1 b8 60 00 00 00 2b fe 80 01 40 80 00 00 00 28 00 10 31 4c 00 1a 01 00 02 80 3b 66 78 01 00 00 07 5c 00 06 04
+00 00 00 02 0a 0e 00 00 00 00 22 07 ce 00 ea 00 00 00 00 00 60 00 03 28 0e 01 d0 00 01 2e 03 7c 40 00 20 00 29 f6 ef 00 00 40
+00 00 00 0a bb 66 07 a8 00 00 20 56 e3 01 00 00 00 00 00 06 00 10 30 15 df 0c a0 00 02 04 d7 f4 32 84 00 00 0d dc 78 80 00
+00 00 20 00 0b f6 c4 7d 00 00 01 7f 4f 50 a0 00 00 00 00 00 00 00 23 28 2a f8 e0 00 00 21 c3 f8 44 00 00 00 09 ee b9 00 00 00
+00 00 04 08 33 b6 00 40 20 11 20 de c7 88 14 80 00 00 00 00 00 80 2b df b3 9c 00 c0 02 02 bf 40 00 40 00 03 55 5c 00 90 10
+00 00 00 80 67 0e 44 3c 00 00 01 9c 3e 59 e0 04 00 00 00 00 00 00 00 14 0d 09 00 02 00 21 af ad 42 40 40 00 36 87 df 09 0c 80
+00 00 00 2a 57 26 e6 28 10 00 01 6e e9 1e 00 00 00 00 00 07 00 00 2a d6 a7 89 00 80 02 04 b7 e0 7e 80 00 00 00 00 80 04 04
+00 00 00 02 55 0b 5c 01 02 00 03 6c 6f 51 f0 00 00 00 00 00 00 02 00 2f 4e 00 80 10 00 21 c3 ef 43 10 40 00 00 00 0f 09 00 00
+00 00 00 2a f5 e3 c3 84 53 10 20 c5 cd 8a 00 00 00 00 00 00 00 00 00 00 05 a8 54 00 02 00 1f 60 60 00 20 00 00 00 01 80 00
+00 00 00 02 05 0b ce 00 00 30 11 9c 3e 50 00 00 00 00 00 02 80 00 00 00 00 09 00 00 00 20 27 bc c2 04 23 00 00 00 00 00 04 00
+00 00
+11 03
+01 01
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 70 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 38 00 80 00 00 00 00 00 02 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 30 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 08 00 00 00 00 00 00 20 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 08 00 00 00 00 00 00 20 00 00 00 00 0c 00 00 00 00 00 00 30 00 00 00 00 00 00 40 00 00 00 00 00 03 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 28 00 02 00 00 00 00 00 00 08
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 08 00 00 00 00 00 00 20 00 00 00 00 00 00 80
+00 00 00 00 00 00 00 00 00 00 00 18 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 00 01 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3c 02 f0 08 00 0c 00 f0 06 40 00 00 00 01 e0 5b 00 00 00
+80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3c f8 00 10 00 80 00 f5 bc 06 80 00 00 12 fd d0 1c 00 0c
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0b 10 00 00 00 f0 0a c4 00 00 00 0b 69 5e 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 81 80 00 00 00 f7 26 60 00 00 00 b0 36 90 08 14 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 8c 02 d9 08 00 00 00 33 b6 42 00 00 04 08 94 19 10 80 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 01 e0 00 00 00 00 00 00 01 09 e9 6f 98 10 00 00 0c c9 6d c0 00 00 10 8b 9e 88 1c 00 00
+00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 21 42 87 10 00 00 00 69 d4 14 28 00 00 03 00 0a 00 c4 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 14 ec 81 80 00 00 16 99 72 06 c0 00 10 30 8e 90 00 a0 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 b5 00 00 00 00 69 b0 14 00 00 00 01 54 0b b0 80 00
+00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 15 6b d8 e0 00 00 16 99 7b 42 80 00 10 33 06 b8 0b 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 8c 0f e0 00 00 08 00 69 b4 02 28 00 00 03 c2 ee 00 00 00
+c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 eb 7c 01 a0 00 c0 16 91 68 e0 40 00 00 38 77 83 98 01 08
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ae 1c d0 0a 00 00 00 69 34 00 00 00 00 09 4e 4a 00 90 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 63 cb 00 10 01 80 16 91 29 40 00 00 01 a8 de 10 19 00 18
+00 00 00 00 00 00 00 00 00 04 3c 00 00 00 00 00 00 00 00 00 00 00 b9 c6 e0 0a 00 00 00 69 30 00 00 05 00 49 fc de 50 04 00
+00 00 00 00 00 00 00 00 00 00 03 c0 e0 00 00 00 00 00 00 00 00 00 0b dc fe 00 10 00 00 16 90 28 06 00 20 00 8f d6 91 8a 80 00
+00 00 00 00 00 00 42 20 02 00 06 7c d1 90 00 08 00 00 00 08 00 00 19 c1 80 10 00 0e 02 22 0a 02 00 00 30 0b c0 00 00 00 00
+80 00 00 00 00 00 20 05 00 22 02 9c cf 10 e0 00 c0 00 00 00 00 05 02 0d 58 00 80 00 8c 2d d3 64 67 00 03 40 bc 1c 00 20 00 0c
+00 00 01 26 90 40 02 00 00 00 19 fd b9 00 00 00 00 00 00 00 00 00 11 46 c9 90 20 00 00 87 0c 04 01 04 00 03 25 69 01 00 00
+00 00 01 06 69 17 80 64 00 04 00 97 5d 01 80 00 00 00 00 00 04 00 02 3d ca 10 84 00 04 08 71 3a 00 00 20 00 0d 95 80 08 00 00
+00 00 09 a6 90 00 00 00 00 40 17 ad f0 8f 00 00 00 00 00 00 00 00 06 47 ff 00 00 00 00 0f 0b c6 00 00 00 02 5a 6d 00 b0 00
+00 00 00 03 69 16 80 00 00 02 00 31 f9 30 10 00 00 00 00 00 00 00 02 9c dc 80 00 00 00 0a 51 2b 40 00 00 00 0f 27 b5 81 00 00
+00 02 00 66 90 03 c0 00 00 04 0b a7 e0 0f 00 00 00 00 00 00 00 20 2b 47 91 09 04 00 00 dd 0e c4 00 00 00 00 cc 39 00 00 00
+00 00 01 07 69 17 80 00 00 08 00 96 df 78 10 00 00 00 00 00 00 00 00 32 6a 80 02 10 04 02 25 be 00 00 00 30 33 0d c5 80 00 00
+00 00 00 a6 90 40 00 00 00 00 80 00 a9 09 00 00 00 00 00 00 00 80 16 d7 b0 9c 00 00 00 87 0b 54 2c 00 00 03 50 4b 50 00 00
+00 00 20 93 69 12 80 60 00 00 18 15 e9 d0 10 00 00 00 00 00 00 00 00 0f 68 38 90 00 04 04 b7 86 60 00 00 00 3a 34 00 0a 00 00
+00 00 01 a6 90 40 40 00 02 00 80 02 e0 08 00 08 00 00 00 00 00 00 80 02 f3 0c 20 0c 00 f0 08 00 40 40 30 03 c0 3a 51 01 40
+00 00 00 13 69 12 a6 00 00 30 28 14 d9 00 10 00 80 00 00 00 00 00 08 14 7a 18 a4 00 80 0f 01 80 c2 80 02 00 03 cf b0 0a 00 00
+00 00 09 26 90 00 03 80 00 00 80 00 bb 90 00 00 00 00 00 00 00 80 80 00 9b 8c 00 00 00 22 5e 00 6c 00 00 03 e8 1d 59 00 00
+00 00 00 13 69 13 80 04 00 60 08 15 e9 99 80 01 80 00 00 00 00 00 08 14 7f b8 10 01 80 0d db b4 62 80 06 00 17 d7 95 1c 00 18
+00 00 0b 26 90 00 00 04 00 00 80 03 ef 0e a8 00 00 00 00 08 a0 00 80 02 e7 09 00 00 00 00 00 36 00 00 00 4b c0 20 d0 00 00
+00 00 00 93 69 13 80 00 00 00 08 14 d9 00 04 c0 00 00 00 00 00 00 18 14 7a 00 80 00 00 00 00 01 40 00 00 00 bc 0e 00 00 00 00
+00 00 01 a6 90 40 02 05 02 00 80 0d e0 00 20 08 00 00 00 00 00 04 34 4f b0 00 00 0e 00 67 0e 40 7c 00 30 03 48 10 70 00 00
+80 00 00 03 69 17 86 04 00 30 0a 06 1e 58 a4 00 80 00 00 00 00 08 02 87 f8 38 e0 04 c4 08 31 f4 e2 00 03 10 34 97 ed 00 00 0c
+00 00 00 a6 90 40 22 00 00 00 80 00 c0 2f 00 00 00 00 00 00 00 00 05 cd 81 0e 40 00 00 43 36 80 30 00 00 00 3c 2d 10 e0 00
+00 00 01 17 69 16 8e 64 80 04 08 15 dc 50 b0 00 00 00 00 00 00 04 00 9e 6f 80 10 00 00 06 7b 60 02 00 00 40 3c 1c e8 18 00 00
+00 00 08 26 90 00 03 82 00 00 80 8c e5 0a 00 00 00 00 00 17 20 80 24 ec e0 99 00 00 10 67 9a cc 3c 00 00 00 e4 2f 00 90 00
+00 00 01 17 69 16 80 00 20 04 08 07 c0 01 c0 c0 00 00 00 02 85 00 02 8c e8 19 80 00 00 02 5b 70 02 00 00 10 32 4c 80 00 00 00
+00 02 09 66 90 00 00 00 00 04 80 00 d5 0a 00 00 00 00 00 00 00 40 2b 67 f0 00 00 02 00 67 02 c4 00 60 08 02 80 2d 90 d2 00
+00 00 00 97 69 17 80 00 00 00 08 15 dc 00 00 00 00 00 00 00 40 04 00 32 eb 78 00 00 04 08 37 76 06 00 00 00 01 5c e8 01 40 00
+00 00 00 66 90 40 40 00 00 00 80 8e e0 00 00 00 00 00 00 03 00 00 12 b7 d7 00 00 00 00 89 72 56 30 10 02 01 7c 2f 00 00 00
+00 00 20 03 69 12 a0 60 50 02 08 07 c0 00 00 02 00 00 00 00 60 00 42 2e 5f 30 c0 00 00 0c 79 a5 c0 00 00 00 2b cc 00 18 00 00
+00 00 01 26 90 40 40 00 03 04 80 00 c0 00 00 28 00 00 00 00 00 00 80 03 ab 10 10 0c 02 05 0b 94 32 80 20 09 38 ee 00 80 0a
+00 00 00 03 69 12 a6 00 00 30 08 15 dd 00 00 02 80 00 00 00 05 00 08 14 fa 99 e0 00 80 20 05 a8 02 90 02 00 a3 5c 00 01 00 60
+00 00 01 26 90 00 00 00 00 00 80 8c e0 0d 00 00 00 00 00 00 00 00 80 02 d7 1b 00 00 02 00 06 ee 28 00 00 08 28 0a 00 00 00
+00 00 00 03 69 13 80 00 00 60 48 07 e0 00 10 01 80 00 00 00 00 00 08 14 7b 00 b0 01 80 2a 07 7f c0 00 06 00 ab dc c0 00 00 18
+00 00 02 26 90 00 00 00 00 00 80 00 df 00 00 00 00 00 00 00 00 00 80 03 bb 08 00 00 02 05 0b bc 20 00 00 0a bc 2c 00 90 08
+00 00 01 03 69 13 80 00 00 00 48 15 dd 80 00 00 00 00 00 00 00 00 08 14 fb 80 12 00 00 20 05 ea 00 40 00 00 81 5e a0 00 01 40
+00 00 00 26 90 40 02 00 23 00 81 43 e0 00 00 0c 00 00 00 00 00 00 80 01 90 0c 00 08 10 00 07 82 34 00 30 0a a8 10 01 00 00
+80 00 00 03 69 17 86 74 00 34 08 01 ca 78 00 00 80 00 00 00 00 00 0a 80 f9 38 10 00 90 0a 03 f8 60 40 22 00 aa 9f a0 08 00 0c
+00 00 00 26 90 40 00 16 20 80 82 82 80 8e 00 00 00 00 00 00 00 00 82 82 a0 00 08 01 00 00 03 c2 38 84 00 09 37 6e 10 04 00
+00 00 00 03 69 16 86 62 01 00 08 00 ce 78 01 00 00 00 00 00 05 00 08 00 7f 30 00 00 20 00 57 7d e0 10 20 00 a3 b6 18 18 a0 00
+00 00 00 26 90 00 00 08 00 00 81 43 e0 09 00 00 00 00 00 00 00 00 80 00 93 0c 09 41 00 a0 07 80 34 00 0a 03 fc 3b 00 f0 00
+00 00 00 03 69 16 80 01 00 02 08 01 c8 00 00 00 00 00 00 00 80 00 0a 80 eb 80 18 08 00 00 03 f8 40 40 00 50 3b d6 b0 01 00 00
+00 02 01 26 90 00 00 00 04 00 82 82 8f 80 00 10 00 00 00 00 00 80 82 82 d0 0d 00 10 02 01 0a 1c 00 00 40 02 bc 10 00 00 01
+00 00 00 03 69 17 80 00 04 00 08 00 cf 78 00 00 00 00 00 00 01 00 08 00 7f 00 10 00 04 20 23 80 00 00 00 00 2b d4 e0 00 00 00
+00 00 00 26 90 40 40 08 00 00 81 43 e0 00 00 00 00 00 00 00 10 80 80 00 90 10 00 02 14 05 07 80 00 00 00 08 3d 6f 10 01 40
+00 00 20 03 69 13 a0 61 70 00 08 00 6e 01 c0 00 00 00 00 02 00 00 0a 80 ed 01 e0 00 00 00 03 f8 02 28 00 00 a3 94 e9 80 00 00
+00 00 00 26 90 40 40 00 03 04 82 82 a0 90 00 08 00 00 00 00 00 00 82 82 80 10 08 0c 02 05 0a 00 00 00 30 0b 30 20 00 80 00
+00 00 00 03 69 12 bc 00 00 20 08 00 ce 10 80 00 c0 00 00 00 00 04 08 00 7e 01 e0 00 c0 20 01 60 00 00 03 10 b3 1c 01 81 00 00
+00 00 00 26 90 00 20 08 00 04 81 43 e9 09 18 00 00 00 00 05 00 80 80 01 97 0f 00 00 02 00 06 40 66 20 00 00 00 1b 00 00 80
+00 00 00 03 69 13 9e 01 00 60 08 01 ee 00 00 01 80 00 00 00 00 00 0a 80 ff 58 10 01 80 2a 01 b0 03 80 06 00 01 5f b0 00 08 18
+00 00 00 26 91 40 02 00 00 40 82 82 c1 08 00 00 00 00 00 00 80 04 82 82 99 08 a0 02 10 ff 02 00 24 00 00 08 3c 1e 00 00 00
+00 00 00 03 69 06 00 04 20 00 08 00 cf 00 00 00 00 00 00 00 00 00 08 00 7e 00 00 00 01 0a f7 78 60 00 00 00 aa 94 e0 00 00 00
+00 00 00 00 00 00 c7 50 40 04 19 c0 90 9e 00 00 00 00 00 00 00 04 18 c2 87 18 00 08 02 00 02 00 00 00 20 02 00 1a 70 f0 00
+80 00 00 00 00 00 00 70 02 01 02 0d ed 38 80 00 00 00 00 00 00 01 02 8d e9 10 80 00 80 aa 07 2d c0 00 02 10 00 0e bf 8a 00 08
+00 00 00 00 00 01 e3 40 00 00 06 6c c0 0d 00 00 00 00 00 00 00 00 09 bf e1 8e 00 00 06 05 02 24 42 00 00 02 80 00 00 00 00
+00 00 00 00 00 00 0c 38 00 00 02 9d cb 38 10 00 00 00 00 00 70 00 02 9e e9 b8 f0 00 00 20 01 f1 c2 10 00 00 28 1f c0 00 00 00
+00 00 08 80 00 72 c3 88 10 04 19 c0 90 0b 00 00 00 00 00 00 00 04 24 df f0 90 1c 00 02 00 06 1c 41 40 08 11 78 5c 01 f0 00
+00 00 00 00 a5 03 20 01 30 01 02 0d 5b 00 14 c0 00 00 00 00 30 00 02 8d fa 70 a0 00 08 2a 07 74 0f 00 00 00 03 3e b0 1f 00 00
+00 00 00 0f f3 a9 e3 42 04 00 31 8e 89 0e 00 10 00 00 00 00 00 04 19 c2 f7 00 00 10 02 05 06 00 00 00 40 00 94 19 00 04 01
+00 00 00 00 ef 77 ee 74 00 00 03 16 7b 00 00 00 00 00 00 00 40 01 02 0d 69 80 00 00 08 20 01 b8 00 00 00 50 29 4d b3 9e 20 00
+00 00 00 0f ff 60 03 08 00 00 26 c3 80 0b 40 80 00 00 00 00 00 00 06 5d 9d 90 00 00 12 00 06 00 02 01 00 02 bc 0a 10 c0 00
+00 00 00 00 ef fe 40 3d 00 01 42 0c 4c 78 c0 14 00 00 00 00 00 00 02 9f 6a 51 e0 10 00 2a 07 7c 00 38 20 00 17 ce 18 00 00 00
+00 00 00 8f f0 60 00 00 00 00 31 8f b0 98 00 00 00 00 00 00 00 00 82 81 f0 8f 00 0a 02 05 06 24 3d 00 30 08 00 1e 08 02 00
+80 00 00 4c ef 5a 40 00 00 00 03 16 68 19 e0 00 00 00 00 00 20 00 08 00 fe 78 10 00 c0 20 01 a8 07 a0 03 20 81 47 f5 80 40 08
+00 00 00 0f f0 77 66 08 40 04 19 c3 90 89 00 00 00 00 00 00 00 00 90 14 c0 1d 00 00 02 00 06 00 78 d0 00 28 00 18 70 d0 00
+00 00 00 40 ef 77 14 2d 02 01 02 0d fb 10 10 00 00 00 00 00 00 00 0a 01 c1 00 b0 01 90 2a 07 24 02 80 06 00 81 5f f0 1d 00 18
+00 00 00 0f ff b0 00 00 00 00 31 8e ff 90 00 00 00 00 00 28 00 00 82 81 9f 08 00 00 12 05 02 3c 3c 00 02 08 00 1e f0 00 00
+00 00 00 00 ef 1b c6 00 00 00 03 16 7e d0 e0 40 00 00 00 01 00 00 08 00 fe 91 90 10 00 20 01 a8 00 40 00 00 81 46 d8 00 00 00
+00 00 00 20 0f 3b c4 00 03 00 18 c3 90 8e 00 00 00 00 00 00 00 20 80 01 d0 00 40 4c 82 00 0b ec 3c 30 30 08 3c 20 b1 b0 00
+80 00 00 02 24 82 20 38 20 25 02 8c f8 59 e0 00 00 00 00 00 00 00 08 15 7c 01 c0 08 c0 20 53 be 00 40 12 20 83 d6 00 1d 1c 0c
+00 00 01 20 a0 70 03 c0 20 00 06 6e f7 0f 00 00 00 00 00 00 00 80 80 02 90 00 20 02 02 00 0f 42 40 01 00 0b c0 20 98 00 00
+00 00 00 02 00 77 80 05 04 00 02 9e e9 00 00 00 00 00 00 01 00 00 08 14 d9 00 00 00 00 20 57 a4 42 00 00 00 bc 1c 03 80 00 00
+00 00 00 20 8f b8 c3 50 40 40 26 c3 e9 90 00 00 00 00 00 10 00 00 80 a4 d7 00 00 00 12 00 0f fc 00 00 00 00 25 fc 00 c0 00
+00 00 00 06 04 90 00 00 02 01 02 0d 7c 39 c0 00 00 00 00 02 80 08 08 07 60 00 00 00 00 20 53 a6 00 00 00 02 01 7c a0 00 00 00
+00 02 00 20 a0 62 c3 20 00 00 06 6c f5 0f 00 00 00 00 00 00 00 00 80 03 a0 80 00 00 06 00 0b 40 00 00 00 01 00 0f 30 00 00
+00 00 00 02 00 77 e0 00 00 00 02 9e eb 81 b0 00 00 00 00 00 00 00 18 14 db 38 00 08 00 20 57 fc 00 00 00 50 21 5e c3 98 00 00
+00 00 00 20 8f b8 04 10 10 00 18 c2 bf 0b 00 00 00 00 00 00 00 80 80 a4 df 80 10 00 02 00 0f c0 40 00 00 01 4c 0d 10 04 00
+00 00 20 02 04 b8 00 38 f2 09 02 8c ed b0 12 00 00 00 00 00 00 00 08 05 60 79 c0 00 00 60 53 e4 02 00 50 10 28 de 0b 00 00 00
+00 00 00 20 a0 78 00 12 23 00 06 6e f0 09 41 40 00 00 00 20 00 00 80 02 80 90 40 0c 02 00 0b 5c 00 00 30 00 14 3d 30 00 00
+00 00 00 02 00 77 de 01 a4 20 02 9f cd 00 1a 00 00 00 00 00 00 08 08 14 d9 71 ee 0a 80 60 57 a6 00 10 03 00 2b de a1 80 00 00
+00 00 00 20 8f b8 03 c8 00 04 18 c2 a3 80 00 00 00 00 00 00 00 00 80 00 d7 0f 00 00 02 00 0b fc 3c 02 00 02 8c 10 00 00 00
+00 00 00 02 04 b0 00 01 00 61 02 8c ff 3a 02 00 00 00 00 00 00 00 28 15 6a f9 d0 01 80 60 53 e4 08 00 56 10 28 df a0 0e 00 18
+00 00 00 20 a0 71 c3 80 00 00 09 ad d0 90 00 00 00 00 00 00 80 01 80 02 b0 80 00 80 02 00 0f 7e 00 01 00 00 14 1d 00 00 00
+00 00 00 02 00 77 c0 04 02 00 02 9f f8 71 80 00 00 00 00 00 00 00 08 14 d9 78 00 10 00 a0 57 ad c7 80 00 00 2b c7 a0 00 80 04
+00 00 00 20 00 f0 20 05 02 00 2b f6 89 0d 00 00 00 00 00 00 00 04 09 85 cb 1d 10 08 00 cb 0b 74 00 00 20 00 00 00 00 00 00
+00 00 00 02 05 36 4c 00 00 32 02 04 d8 58 00 00 00 00 00 00 00 00 00 97 cb f0 80 10 c4 08 93 bb e7 00 03 00 00 00 03 80 00 00
+00 00 08 0f f1 e1 c0 00 00 00 19 c1 a7 0e 00 00 00 00 00 00 00 00 29 c5 ab 89 00 00 00 e7 0b 82 20 00 00 00 2d 4f b1 00 00
+00 00 00 00 ef b7 3c 00 00 01 02 0d 58 80 10 00 00 00 00 00 60 00 00 04 fb f9 91 02 04 00 55 29 c2 04 40 00 2a cc 89 08 00 00
+00 00 08 20 00 b0 03 8a 20 00 09 86 d3 19 00 20 00 00 00 00 00 00 01 41 c0 0d 00 40 00 e2 07 ec 74 00 00 01 7c 28 11 00 00
+00 00 00 02 05 32 5c f9 30 00 02 9c f8 50 c0 00 00 00 00 00 00 04 02 bc 6d 00 90 00 04 08 15 b6 43 40 00 02 01 5f 90 08 00 00
+00 02 08 20 00 69 e0 00 00 00 19 c3 ab 0a 00 00 00 00 00 20 20 00 81 42 ef 1d 00 00 00 5f 0e dc 38 05 00 23 28 2f 91 80 00
+00 00 00 02 05 3b ac 00 00 01 02 0d de 58 00 00 00 00 00 00 05 00 0a bc 5f 01 94 00 00 00 55 f6 e0 00 00 10 30 1d d0 08 00 00
+00 00 00 20 00 b0 00 00 00 00 22 44 80 0e 00 80 00 00 00 00 50 80 82 82 8d 1e 00 00 00 6e 9a 4e 00 00 00 03 14 20 00 00 00
+00 00 20 02 05 36 40 00 45 00 03 1e ec 00 10 14 00 00 00 00 02 00 0a bd fe 80 90 00 00 08 59 31 c7 80 00 10 31 44 80 1c 00 00
+00 00 00 20 00 74 00 17 03 02 19 c0 a9 1e 40 00 00 00 00 00 00 04 81 42 e0 00 00 0c 02 0a 02 0c 38 01 30 03 2c 0c 01 0a 00
+00 00 00 06 05 3b 00 38 80 31 02 0d da 01 88 00 00 00 00 00 00 00 0a bd cf 10 00 00 c0 20 03 ba 00 00 03 10 22 46 80 0c 00 00
+00 00 00 20 2b b2 c3 85 00 00 19 c6 e0 1c 80 40 00 00 00 00 00 00 82 80 a0 18 20 00 12 08 73 02 00 00 00 01 5e 6c 01 e8 00
+00 00 00 02 01 b0 60 00 00 60 00 95 f9 79 f0 00 00 00 00 00 00 00 0a bc df 00 94 81 80 20 47 85 c0 00 06 00 28 4c e5 9f 00 00
+00 00 00 20 00 7c 03 80 00 20 06 55 80 10 0c 00 00 00 00 00 00 00 81 42 e0 18 00 00 02 0a 03 c6 38 60 00 01 3e ea f1 80 00
+00 00 00 02 05 3b c0 00 00 00 02 9c fd 01 80 00 00 00 00 01 40 00 0a bd ed 59 b0 08 10 20 03 bf c0 40 00 00 11 66 9d 89 04 00
+00 00 08 20 a0 f0 47 80 03 00 01 42 b0 80 20 08 00 00 00 03 00 00 00 00 00 8c 00 00 10 c5 00 34 00 00 02 0a 9c 39 b0 00 04
+80 00 00 02 af 5e 20 38 10 32 02 bd 6e 10 04 00 80 00 00 00 00 00 00 00 00 50 10 00 04 0c 55 a8 40 13 50 30 89 55 b8 00 00 8c
+00 00 00 60 50 b0 44 00 00 00 81 41 c7 0a 40 00 00 00 08 10 00 00 00 82 ef 09 80 40 00 45 de 02 7c 00 00 40 14 10 00 00 00
+00 00 00 02 af 7b 20 78 00 00 0a bc c8 71 90 00 00 00 00 02 00 01 00 1c d8 10 84 00 00 0e 53 f8 43 c0 00 20 01 47 e7 00 10 00
+00 00 00 60 a0 38 04 00 08 40 01 42 b1 00 00 40 00 00 00 10 00 00 31 c1 eb 8c 44 00 10 5f 0f 00 42 00 00 28 3a d9 19 ea 00
+00 00 00 02 af 5b 40 68 00 00 02 bc e8 f0 00 00 00 00 00 02 00 01 02 24 7d f8 10 00 00 00 55 bd c3 80 00 40 a3 d7 c9 09 00 00
+00 02 01 20 50 30 c7 c0 00 00 81 42 cb 8a 80 00 00 00 00 00 00 00 19 ce d1 8b 00 00 10 50 02 ee 00 00 00 0b 0c 5d 18 04 00
+00 00 00 02 af 5f 00 68 00 00 1a bc ea 11 f0 80 00 00 00 00 02 00 00 97 4e b8 10 04 00 0a 03 07 c7 00 00 01 91 f4 dd 98 80 00
+00 00 00 20 a0 e4 02 00 10 20 81 43 e0 0b 00 00 00 00 00 10 40 00 00 6f 87 00 40 00 00 aa 0b 86 78 70 00 08 3a fc 00 00 00
+00 00 20 06 af 56 06 64 00 00 0a bd d8 78 06 00 00 00 00 00 02 00 02 0d f9 00 0b 80 00 8a 05 e0 42 00 50 01 a3 c7 d1 98 a1 10
+00 00 00 20 50 f0 02 00 00 04 81 43 c7 0e 1c 08 00 00 00 18 08 00 20 43 9b 00 0c 00 00 05 f2 36 40 00 04 4b 0e fd 01 90 00
+00 00 00 12 af 72 06 74 00 00 0a bd d8 02 10 00 80 00 00 02 00 01 02 2c fa 80 00 40 00 8e 7f 6f c7 00 00 00 91 ce d5 9c 00 00
+00 00 01 ac 21 ba c0 00 05 00 81 43 ed 8e 00 00 00 00 00 00 00 00 17 c2 9d 0e 10 00 00 17 df 7c 30 24 08 2b 0c fd f9 81 01
+40 00 00 0a c7 32 e0 00 02 50 0a bd ec 70 14 43 80 00 00 00 60 00 00 15 cc 01 c0 00 00 02 71 b2 00 43 08 00 91 d5 cb 9d 00 30
+00 00 00 a0 d5 74 00 00 08 00 81 41 cb 88 00 80 00 00 00 00 c0 00 02 a6 89 af 00 80 00 2b 7e 4e 3c 02 0c 38 23 eb 00 00 40
+00 00 00 0a 8f 52 0e 60 00 00 0a bd 6c 78 10 04 00 00 00 01 52 08 02 2d ce 58 00 04 00 02 71 6a c8 68 00 61 80 0d 87 00 00 00
+00 00
+62 00 3f
+72 00 80
+11 00
+82 00 00
+01 03
+00 00 00 00 c0 00 00 d7
+00 00 00 00 c6 66 00 d7
+00 00 00 00 cc cd 00 d7
+00 00 00 00 d3 33 00 d7
+00 00 00 00 d9 9a 00 d7
+00 00 00 00 e0 00 00 d7
+00 00 00 00 e6 66 00 d7
+00 00 00 00 ec cd 00 d7
+00 00 00 00 f3 33 00 d7
+00 00 00 00 f9 9a 00 d7
+00 00 00 00 00 00 00 d8
+00 00 00 00 06 66 00 d8
+00 00 00 00 0c cd 00 d8
+00 00 00 00 13 33 00 d8
+00 00 00 00 19 9a 00 d8
+00 00 00 00 20 00 00 d8
+00 00 00 00 26 66 00 d8
+00 00 00 00 2c cd 00 d8
+00 00 00 00 33 33 00 d8
+00 00 00 00 39 9a 00 d8
+00 00 00 00 40 00 00 d8
+00 00 00 00 46 66 00 d8
+00 00 00 00 4c cd 00 d8
+00 00 00 00 53 33 00 d8
+00 00 00 00 59 9a 00 d8
+00 00 00 00 60 00 00 d8
+00 00 00 00 66 66 00 d8
+00 00 00 00 6c cd 00 d8
+00 00 00 00 73 33 00 d8
+00 00 00 00 79 9a 00 d8
+00 00 00 00 80 00 00 d8
+00 00 00 00 86 66 00 d8
+00 00 00 00 8c cd 00 d8
+00 00 00 00 93 33 00 d8
+00 00 00 00 99 9a 00 d8
+00 00 00 00 a0 00 00 d8
+00 00 00 00 a6 66 00 d8
+00 00 00 00 ac cd 00 d8
+00 00 00 00 b3 33 00 d8
+00 00 00 00 b9 9a 00 d8
+00 00 00 00 c0 00 00 d8
+00 00 00 00 c6 66 00 d8
+00 00 00 00 cc cd 00 d8
+00 00 00 00 d3 33 00 d8
+00 00 00 00 d9 9a 00 d8
+00 00 00 00 e0 00 00 d8
+00 00 00 00 e6 66 00 d8
+00 00 00 00 ec cd 00 d8
+00 00 00 00 f3 33 00 d8
+00 00 00 00 f9 9a 00 d8
+00 00 00 00 00 00 00 d9
+00 00 00 00 06 66 00 d9
+00 00 00 00 0c cd 00 d9
+00 00 00 00 13 33 00 d9
+00 00 00 00 19 9a 00 d9
+00 00 00 00 20 00 00 d9
+00 00 00 00 26 66 00 d9
+00 00 00 00 2c cd 00 d9
+00 00 00 00 33 33 00 d9
+00 00 00 00 39 9a 00 d9
+00 00 00 00 40 00 00 d9
+00 00 00 00 46 66 00 d9
+00 00 00 00 4c cd 00 d9
+00 00 00 00 53 33 00 d9
+00 00 00 00 59 9a 00 d9
+00 00 00 00 60 00 00 d9
+00 00 00 00 66 66 00 d9
+00 00 00 00 6c cd 00 d9
+00 00 00 00 73 33 00 d9
+00 00 00 00 79 9a 00 d9
+00 00 00 00 80 00 00 d9
+00 00 00 00 86 66 00 d9
+00 00 00 00 8c cd 00 d9
+00 00 00 00 93 33 00 d9
+00 00 00 00 99 9a 00 d9
+00 00 00 00 a0 00 00 d9
+00 00 00 00 a6 66 00 d9
+00 00 00 00 ac cd 00 d9
+00 00 00 00 b3 33 00 d9
+00 00 00 00 b9 9a 00 d9
+00 00 00 00 c0 00 00 d9
+00 00 00 00 c6 66 00 d9
+00 00 00 00 cc cd 00 d9
+00 00 00 00 d3 33 00 d9
+00 00 00 00 d9 9a 00 d9
+00 00 00 00 e0 00 00 d9
+00 00 00 00 e6 66 00 d9
+00 00 00 00 ec cd 00 d9
+00 00 00 00 f3 33 00 d9
+00 00 00 00 f9 9a 00 d9
+00 00 00 00 00 00 00 da
+00 00 00 00 06 66 00 da
+00 00 00 00 0c cd 00 da
+00 00 00 00 13 33 00 da
+00 00 00 00 19 9a 00 da
+00 00 00 00 20 00 00 da
+00 00 00 00 26 66 00 da
+00 00 00 00 2c cd 00 da
+00 00 00 00 33 33 00 da
+00 00 00 00 39 9a 00 da
+00 00 00 00 40 00 00 da
+00 00 00 00 46 66 00 da
+00 00 00 00 4c cd 00 da
+00 00 00 00 53 33 00 da
+00 00 00 00 59 9a 00 da
+00 00 00 00 60 00 00 da
+00 00 00 00 66 66 00 da
+00 00 00 00 6c cd 00 da
+00 00 00 00 73 33 00 da
+00 00 00 00 79 9a 00 da
+00 00 00 00 80 00 00 da
+00 00 00 00 86 66 00 da
+00 00 00 00 8c cd 00 da
+00 00 00 00 93 33 00 da
+00 00 00 00 99 9a 00 da
+00 00 00 00 a0 00 00 da
+00 00 00 00 a6 66 00 da
+00 00 00 00 ac cd 00 da
+00 00 00 00 b3 33 00 da
+00 00 00 00 b9 9a 00 da
+00 00 00 00 c0 00 00 da
+00 00 00 00 c6 66 00 da
+00 00 00 00 cc cd 00 da
+00 00 00 00 d3 33 00 da
+00 00 00 00 d9 9a 00 da
+00 00 00 00 e0 00 00 da
+00 00 00 00 e6 66 00 da
+00 00 00 00 ec cd 00 da
+00 00
+82 00 80
+01 03
+00 00 00 00 f3 33 00 da
+00 00 00 00 f9 9a 00 da
+00 00 00 00 00 00 00 db
+00 00 00 00 06 66 00 db
+00 00 00 00 0c cd 00 db
+00 00 00 00 13 33 00 db
+00 00 00 00 19 9a 00 db
+00 00 00 00 20 00 00 db
+00 00 00 00 26 66 00 db
+00 00 00 00 2c cd 00 db
+00 00 00 00 33 33 00 db
+00 00 00 00 39 9a 00 db
+00 00 00 00 40 00 00 db
+00 00 00 00 46 66 00 db
+00 00 00 00 4c cd 00 db
+00 00 00 00 53 33 00 db
+00 00 00 00 59 9a 00 db
+00 00 00 00 60 00 00 db
+00 00 00 00 66 66 00 db
+00 00 00 00 6c cd 00 db
+00 00 00 00 73 33 00 db
+00 00 00 00 79 9a 00 db
+00 00 00 00 80 00 00 db
+00 00 00 00 86 66 00 db
+00 00 00 00 8c cd 00 db
+00 00 00 00 93 33 00 db
+00 00 00 00 99 9a 00 db
+00 00 00 00 a0 00 00 db
+00 00 00 00 a6 66 00 db
+00 00 00 00 ac cd 00 db
+00 00 00 00 b3 33 00 db
+00 00 00 00 b9 9a 00 db
+00 00 00 00 c0 00 00 db
+00 00 00 00 c6 66 00 db
+00 00 00 00 cc cd 00 db
+00 00 00 00 d3 33 00 db
+00 00 00 00 d9 9a 00 db
+00 00 00 00 e0 00 00 db
+00 00 00 00 e6 66 00 db
+00 00 00 00 ec cd 00 db
+00 00 00 00 f3 33 00 db
+00 00 00 00 f9 9a 00 db
+00 00 00 00 00 00 00 dc
+00 00 00 00 06 66 00 dc
+00 00 00 00 0c cd 00 dc
+00 00 00 00 13 33 00 dc
+00 00 00 00 19 9a 00 dc
+00 00 00 00 20 00 00 dc
+00 00 00 00 26 66 00 dc
+00 00 00 00 2c cd 00 dc
+00 00 00 00 33 33 00 dc
+00 00 00 00 39 9a 00 dc
+00 00 00 00 40 00 00 dc
+00 00 00 00 46 66 00 dc
+00 00 00 00 4c cd 00 dc
+00 00 00 00 53 33 00 dc
+00 00 00 00 59 9a 00 dc
+00 00 00 00 60 00 00 dc
+00 00 00 00 66 66 00 dc
+00 00 00 00 6c cd 00 dc
+00 00 00 00 73 33 00 dc
+00 00 00 00 79 9a 00 dc
+00 00 00 00 80 00 00 dc
+00 00 00 00 86 66 00 dc
+00 00 00 00 8c cd 00 dc
+00 00 00 00 93 33 00 dc
+00 00 00 00 99 9a 00 dc
+00 00 00 00 a0 00 00 dc
+00 00 00 00 a6 66 00 dc
+00 00 00 00 ac cd 00 dc
+00 00 00 00 b3 33 00 dc
+00 00 00 00 b9 9a 00 dc
+00 00 00 00 c0 00 00 dc
+00 00 00 00 c6 66 00 dc
+00 00 00 00 cc cd 00 dc
+00 00 00 00 d3 33 00 dc
+00 00 00 00 d9 9a 00 dc
+00 00 00 00 e0 00 00 dc
+00 00 00 00 e6 66 00 dc
+00 00 00 00 ec cd 00 dc
+00 00 00 00 f3 33 00 dc
+00 00 00 00 f9 9a 00 dc
+00 00 00 00 00 00 00 dd
+00 00 00 00 06 66 00 dd
+00 00 00 00 0c cd 00 dd
+00 00 00 00 13 33 00 dd
+00 00 00 00 19 9a 00 dd
+00 00 00 00 20 00 00 dd
+00 00 00 00 26 66 00 dd
+00 00 00 00 2c cd 00 dd
+00 00 00 00 33 33 00 dd
+00 00 00 00 39 9a 00 dd
+00 00 00 00 40 00 00 dd
+00 00 00 00 46 66 00 dd
+00 00 00 00 4c cd 00 dd
+00 00 00 00 53 33 00 dd
+00 00 00 00 59 9a 00 dd
+00 00 00 00 60 00 00 dd
+00 00 00 00 66 66 00 dd
+00 00 00 00 6c cd 00 dd
+00 00 00 00 73 33 00 dd
+00 00 00 00 79 9a 00 dd
+00 00 00 00 80 00 00 dd
+00 00 00 00 86 66 00 dd
+00 00 00 00 8c cd 00 dd
+00 00 00 00 93 33 00 dd
+00 00 00 00 99 9a 00 dd
+00 00 00 00 a0 00 00 dd
+00 00 00 00 a6 66 00 dd
+00 00 00 00 ac cd 00 dd
+00 00 00 00 b3 33 00 dd
+00 00 00 00 b9 9a 00 dd
+00 00 00 00 c0 00 00 dd
+00 00 00 00 c6 66 00 dd
+00 00 00 00 cc cd 00 dd
+00 00 00 00 d3 33 00 dd
+00 00 00 00 d9 9a 00 dd
+00 00 00 00 e0 00 00 dd
+00 00 00 00 e6 66 00 dd
+00 00 00 00 ec cd 00 dd
+00 00 00 00 f3 33 00 dd
+00 00 00 00 f9 9a 00 dd
+00 00 00 00 00 00 00 de
+00 00 00 00 06 66 00 de
+00 00 00 00 0c cd 00 de
+00 00 00 00 13 33 00 de
+00 00 00 00 19 9a 00 de
+00 00 00 00 20 00 00 de
+00 00
+11 01
+82 00 00
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+82 00 80
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+11 02
+82 00 00
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+82 00 80
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+11 03
+82 00 00
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+82 00 80
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+22
+3a 5f
+01 06
+
diff --git a/fpga/SX1301_FPGA_200K_NOTCH_LBT_SPECTRAL_SCAN_915_v33.hex b/fpga/SX1301_FPGA_200K_NOTCH_LBT_SPECTRAL_SCAN_915_v33.hex new file mode 100644 index 0000000..55bea37 --- /dev/null +++ b/fpga/SX1301_FPGA_200K_NOTCH_LBT_SPECTRAL_SCAN_915_v33.hex @@ -0,0 +1,1659 @@ +ff 00
+4c 61 74 74 69 63 65 00
+69 43 45 63 75 62 65 32 20 32 30 31 35 2e 30 34 2e 32 37 34 30 39 00
+50 61 72 74 3a 20 69 43 45 34 30 4c 50 31 4b 2d 43 4d 34 39 00
+44 61 74 65 3a 20 4f 63 74 20 31 37 20 32 30 31 36 20 31 31 3a 33 30 3a 32 32 00
+00 ff
+7e aa 99 7e
+51 00
+01 05
+92 00 20
+62 01 4b
+72 00 90
+82 00 00
+11 00
+01 01
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 02 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 30 00 30 00 00
+00 00 00 00 00 04 00 00 00 00 00 00 10 00 00 00 00 00 00 40 00 00 00 00 10 00 00 00 00 00 00 40 00 00 00 00 00 01 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 04 00 00 00 00 00 00 10 00 00 00 00 00 00 40 00 00 00 00 10 00 00 00 00 00 00 40 00 00 00 00 00 03 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f0 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 10 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00
+00 00 00 00 00 0f 22 13 00 00 00 00 3e 00 02 00 00 01 00 d8 00 00 40 00 21 c5 00 00 00 00 0c 32 21 30 00 00 0c 01 78 f5 20
+00 00 00 00 00 00 70 04 90 80 00 00 02 c0 a5 00 00 00 18 0b 81 00 00 00 01 50 a0 00 00 00 00 03 80 49 48 00 00 00 0e 0a 00 00
+00 00 18 00 00 0f 22 13 00 00 00 03 0f a5 00 01 81 8f 80 78 00 00 00 00 0d a0 50 01 80 50 00 5a 21 30 00 47 2c 00 6a f5 00
+00 00 00 00 09 00 f0 04 90 00 00 00 00 20 50 00 00 00 78 0b 01 00 00 00 00 00 05 00 00 02 00 07 00 49 40 08 02 c0 1d 0a 00 00
+00 00 30 00 01 9d 22 13 00 00 00 00 1e ea 50 00 00 05 88 38 00 00 40 00 21 e5 00 03 00 00 00 7a 21 30 00 00 00 32 cc 80 02
+00 00 01 00 00 08 78 04 90 00 00 00 03 e0 55 00 00 00 00 01 01 00 40 00 01 40 a0 00 10 00 00 03 00 49 00 00 00 00 3d 0e 52 00
+00 02 00 00 19 9d 22 13 00 00 00 c0 0e e5 00 04 00 00 01 f0 00 00 03 c0 01 a0 50 00 00 05 80 02 61 30 00 00 00 05 ce 50 00
+00 00 00 00 00 08 f8 04 90 00 00 00 00 20 50 00 00 00 90 af 80 40 00 3c 32 c0 0a 00 00 00 00 07 30 49 00 00 08 03 41 05 00 80
+00 00 00 00 00 0f a2 13 00 00 00 00 26 00 00 00 00 08 1c f0 10 04 08 06 b7 cf 50 00 00 00 0f 12 21 30 00 00 3c 30 40 00 00
+00 00 20 00 00 09 70 04 90 00 00 00 02 e0 a5 20 00 00 51 03 80 00 00 14 01 60 a0 00 00 00 70 c3 80 49 00 00 01 60 1c 0a 50 00
+00 00 00 00 00 0d a2 13 00 00 01 e0 00 00 00 00 00 09 01 50 10 00 01 c6 97 e5 00 00 00 19 00 3a 21 32 00 00 00 01 dc 50 00
+00 00 00 00 00 00 f0 04 90 00 00 02 24 00 00 00 00 04 50 a1 00 00 00 34 00 10 50 00 00 02 58 07 00 49 00 00 82 40 00 05 02 00
+00 00 00 00 98 0d a2 13 00 00 00 42 2e 00 00 00 00 00 1b 18 10 00 00 00 21 be 20 00 00 00 0a 7a 21 32 00 00 2e 20 00 24 00
+00 00 00 00 01 00 78 04 90 00 00 2c 02 c0 a5 00 00 00 00 ad 00 00 00 00 01 4e d1 00 00 00 70 03 80 49 00 08 01 40 34 e1 80 00
+00 00 10 00 90 0d a2 11 01 00 02 00 00 00 00 01 00 07 00 b0 10 00 02 e3 97 c5 00 03 00 0f 81 72 21 30 00 00 00 70 0f 03 00
+00 00 01 00 01 08 f8 04 90 00 00 1c 24 00 00 00 10 00 08 a5 00 40 00 16 40 00 50 00 30 00 b0 a7 80 49 00 00 00 04 00 b0 30 00
+00 00 00 00 81 9f f3 d5 00 00 00 00 2e ba 44 00 00 01 80 90 01 0a 02 03 8c 00 00 00 00 07 8b fa 21 30 00 00 20 7b 68 a5 00
+00 00 00 00 01 08 9c 28 10 00 00 34 2f fa 80 40 00 00 78 09 00 00 40 2c 00 40 a5 00 00 00 09 f7 80 49 10 00 c2 40 3f 05 50 04
+00 00 1a 80 80 09 7a 81 00 60 01 42 bd ba 14 01 80 08 01 b0 08 00 00 02 05 e5 00 01 80 00 18 d2 21 30 00 00 00 02 0c 50 00
+00 00 00 10 01 0f d0 3d 50 00 00 02 42 ca 02 40 00 00 90 9d 00 00 00 00 40 00 50 00 00 00 01 03 80 49 00 00 00 00 15 0a 01 00
+00 00 30 00 00 0f b3 d5 00 c0 00 12 8e da 44 00 00 50 01 70 00 00 03 60 1c 00 00 01 00 00 1a 92 21 30 00 82 00 69 7c 50 00
+00 00 01 00 00 00 90 28 10 04 00 3c 6b fa 80 40 00 20 00 d1 80 40 00 3c 00 60 a5 00 10 00 00 07 80 49 00 04 00 00 01 05 00 00
+00 02 00 00 00 01 62 95 00 00 02 40 25 b8 54 00 00 03 0c d8 00 00 00 00 04 a5 00 00 23 11 00 82 21 30 00 00 20 02 1c 50 00
+00 00 00 00 0d 0f bc 15 50 00 00 2c 02 e9 40 40 00 00 00 ef 02 00 00 00 00 00 50 00 01 02 08 03 34 49 00 00 02 c2 54 0a 00 00
+00 00 00 00 19 af 23 d5 00 00 06 03 2f 9a 44 00 08 09 00 b8 01 00 25 60 3e 8f 52 00 00 00 09 ba 21 30 00 00 00 7d 7e 50 00
+00 00 20 00 00 0e 9c 28 10 00 00 2c 6d ea 80 40 01 00 50 01 80 00 00 00 02 f0 a0 00 00 00 00 a7 80 49 00 00 00 06 41 05 00 10
+00 00 00 00 19 89 ba 81 00 00 02 06 24 da 44 00 00 05 1a f0 01 00 02 00 05 e5 00 00 10 03 98 b2 21 30 00 00 20 7a 0a 50 08
+00 00 00 00 09 00 d0 3d 50 00 00 2c 3a cf 40 40 00 00 f0 03 02 00 a0 14 20 10 50 00 00 80 01 03 80 49 00 00 03 c4 15 0a 00 00
+00 00 00 00 d8 1f 73 d5 00 00 11 c3 8e ba 44 00 10 25 81 d0 01 0a 00 00 55 8f 50 00 00 00 00 92 21 38 00 00 00 00 de f5 00
+00 00 00 00 05 0a 90 28 10 10 00 80 03 6a 80 40 02 84 00 ad 82 00 00 00 28 50 a0 00 00 00 00 07 00 49 00 00 00 00 07 0a 00 00
+00 00 30 00 d8 18 62 81 00 c0 00 c3 ad c0 50 03 00 05 81 78 00 00 01 66 c5 c5 00 01 20 03 81 fa 21 10 00 00 00 34 00 00 00
+00 00 01 00 05 0a 54 14 10 04 00 1e 25 f0 5a 00 10 00 00 a3 02 00 00 00 34 00 50 00 31 00 00 d9 00 85 00 00 00 03 c0 00 00 00
+00 00 00 00 00 d0 70 14 00 00 01 40 00 12 40 00 00 00 00 00 00 00 01 42 b5 ea 02 80 00 50 00 f7 bd 40 00 00 34 2c 5a e3 60
+c0 00 00 00 00 1f 90 02 80 00 08 02 01 ca 18 00 00 40 00 00 00 00 00 36 6d e0 f5 00 00 00 00 0b 82 80 00 00 63 e6 3c 84 34 00
+00 00 18 00 80 0f f9 58 00 04 00 06 a1 ea 00 00 00 00 00 00 00 10 c0 02 06 8a 00 00 00 00 00 7e b4 40 82 00 14 04 6e a1 40
+00 00 00 00 07 00 b7 08 00 00 20 00 02 50 50 08 00 00 00 00 00 00 00 34 00 10 a0 00 00 00 00 00 03 44 80 a0 e3 62 0d 87 a6 8c
+00 00 30 00 80 00 70 04 08 00 00 00 20 12 40 00 00 00 00 00 00 00 01 40 20 de 20 80 08 01 00 7b 81 40 00 00 04 00 58 c1 50
+00 00 01 00 01 00 90 00 84 40 00 74 01 ca 18 00 00 02 00 00 00 00 00 02 2d db d1 01 01 00 00 90 80 14 40 00 00 20 3c 9c b6 08
+00 02 00 00 18 07 b8 b8 00 0e 01 c0 21 a0 50 00 00 00 00 00 00 00 11 42 95 ea 52 00 00 01 81 02 81 40 00 00 06 02 ee e5 40
+80 00 00 00 00 00 36 6f 40 00 a0 02 02 c0 0a 00 00 00 00 00 00 00 00 02 6d c0 55 00 00 00 00 93 00 28 00 00 03 42 c7 8a 46 14
+00 00 00 00 30 1d 38 41 08 00 00 00 05 8a 02 00 00 00 00 00 00 02 00 e2 21 8a 02 00 00 07 81 32 bd 40 00 a0 06 00 fe a0 40
+00 00 20 00 00 08 50 0c 10 00 00 24 00 10 a0 00 0a 00 00 00 00 00 40 00 00 d0 50 00 00 00 70 cb 42 80 00 04 00 00 04 0f ac 10
+00 00 00 00 30 d7 fc 00 00 02 01 e2 3d ca 50 00 10 20 00 00 00 00 c0 00 1f af 50 00 00 00 09 d3 34 40 80 00 16 01 6e a1 61
+80 00 00 00 00 0e 37 40 40 00 00 0e 43 e0 55 00 00 84 00 00 00 00 00 2c 00 c0 50 08 00 00 70 db 02 80 00 00 00 03 45 9b 54 00
+00 00 00 00 01 eb 76 00 00 00 00 47 86 aa 00 00 10 00 00 00 00 40 00 60 24 98 00 00 00 18 01 02 94 00 00 00 00 20 5b c1 60
+00 00 00 00 03 00 f7 80 00 00 0a 02 40 20 a0 00 80 82 00 00 00 04 00 24 01 5b 00 00 00 00 f0 e5 42 80 20 00 00 04 3d 9c b4 08
+00 00 10 00 79 eb a1 00 09 00 e0 42 ad ca 50 00 00 20 00 00 00 00 01 c0 24 e8 00 30 00 01 00 d7 74 40 4c 00 24 63 ee e5 41
+00 00 01 00 00 10 3c 34 00 00 00 02 64 c0 aa 00 00 05 00 00 00 00 00 24 03 40 00 00 00 00 90 fb 2e 80 00 c0 00 40 37 8a 46 90
+00 00 00 00 18 0d 2a 95 00 00 02 00 5c ca 04 00 00 00 00 00 00 00 02 07 de 8b 7c 80 11 83 1a db bd 40 00 01 26 00 40 ff 40
+00 00 00 00 00 0a 34 17 d0 00 00 3c 20 c0 fa 40 00 00 00 00 00 00 00 04 61 68 12 42 02 00 f1 05 02 80 00 00 03 c0 0c 00 0c 00
+00 00 18 00 f8 01 73 c1 00 60 03 c6 c0 8e 24 00 00 00 00 00 00 60 02 02 9c 99 5c 00 00 00 00 5b a0 04 8a 00 4e 60 00 5a 40
+00 00 00 00 01 80 18 3e 90 00 00 2e 29 dc d1 40 00 00 00 00 00 00 00 04 6f cf 56 40 00 08 00 f1 43 94 00 a0 02 64 04 0a 5c 00
+00 00 00 00 81 85 70 15 02 00 00 c7 80 ec 54 00 03 80 00 00 00 c0 01 46 1e 8b 7c 00 00 40 1e 12 61 40 10 00 40 00 59 01 10
+00 00 00 00 01 0a bc 17 d0 00 00 00 02 48 ca 41 01 00 00 00 00 04 80 36 00 e9 12 40 00 07 01 0f 72 00 00 00 00 00 0c b0 00 00
+00 02 00 00 01 8d 62 81 00 00 00 00 24 8c f0 00 00 00 00 00 00 00 00 66 14 99 5c 00 00 2f 00 03 81 42 00 80 20 61 6c f5 00
+00 00 00 00 00 40 18 3d 52 00 00 0c 00 5f 4b 00 00 00 00 00 00 00 01 00 02 6f 56 40 80 05 f0 07 40 28 00 00 11 c4 02 0f 50 00
+00 00 00 00 80 19 fa 81 00 00 d2 60 16 c8 70 20 10 80 00 00 00 00 00 00 3e 9b 7c 00 00 00 0e fb bd 40 00 00 20 38 18 a0 00
+00 00 20 80 05 08 14 3e 98 02 00 c4 00 00 87 00 02 80 00 00 00 02 80 1c 3b e8 12 48 08 00 00 05 42 80 00 00 08 44 0d 05 00 00
+00 00 00 00 80 85 62 80 00 00 01 c0 14 c8 70 20 00 00 00 00 00 00 0a 00 56 99 5c 00 28 07 80 1b a0 00 80 00 40 01 6c fa 00
+00 00 00 00 0b 10 08 28 0c 00 08 02 00 00 87 00 00 00 00 00 00 00 01 6c 21 4f 56 40 01 00 98 01 c3 94 80 00 08 00 02 0f a0 00
+00 00 00 00 00 09 fb d5 00 00 02 00 4f 88 70 20 00 50 00 00 00 04 00 60 3e 9b 7d 00 00 00 0e 37 3e c0 00 02 04 00 18 fa 00
+00 00 00 20 00 00 d4 14 12 00 00 1c 38 10 87 00 00 00 00 00 00 00 20 00 22 6d 12 40 00 00 11 81 af fc 00 00 00 00 0d 0f 50 00
+00 00 30 00 10 1d b2 81 00 40 00 00 15 88 70 20 00 00 00 00 00 c0 80 03 f6 9d 14 00 00 00 00 83 81 40 04 80 04 00 5f 85 00
+00 00 03 00 00 88 18 3d 50 04 00 00 00 30 87 08 00 00 00 00 00 04 00 00 6b e9 47 40 01 08 f0 05 40 28 20 44 b0 00 2c ad 50 00
+00 00 80 30 d1 88 73 45 00 00 e0 40 26 ec 10 00 00 80 00 00 00 00 02 00 01 ea 00 00 00 0f 01 72 21 30 00 02 1e 03 fd fd 00
+00 00 20 00 01 8d 56 b8 90 00 02 02 00 de c7 00 00 00 00 00 00 00 08 14 01 c0 50 00 00 00 f0 f5 24 49 0c 00 00 00 34 f5 80 80
+00 00 18 00 00 01 6a 95 00 00 21 40 0c ee 22 20 00 00 00 00 00 00 00 07 9e ea 00 01 80 08 01 f2 21 30 00 00 0e 29 ec f5 00
+08 00 04 00 00 00 50 15 50 00 40 02 00 10 e2 00 00 00 00 00 00 10 00 00 00 20 a0 00 00 00 f0 eb 20 49 04 00 00 20 3c 05 00 00
+00 00 10 00 50 ad b3 d5 00 00 00 00 3e cf 50 80 00 00 00 00 00 00 00 00 16 aa 50 0b 00 05 18 12 61 38 00 04 00 2f f8 81 02
+00 00 01 00 05 1b 50 28 10 00 00 00 00 e0 a0 00 00 00 00 00 00 00 00 64 00 40 55 00 10 00 08 05 20 49 00 00 60 06 bc 0d 10 00
+00 01 00 00 38 03 6a 95 00 00 00 00 1e c0 50 80 03 00 00 00 00 00 03 e0 3e ef 52 00 00 01 80 d2 21 30 00 00 60 2b 5e e5 00
+00 00 00 00 0d 80 18 15 50 00 00 00 00 00 05 00 00 00 00 00 00 40 00 1c 03 d0 ff 00 00 00 00 0b a0 49 00 10 0b c4 1f 88 02 00
+00 00 00 00 18 db 23 d5 00 04 03 e2 c1 fb 10 10 00 00 00 00 00 00 01 e0 26 cf 00 00 00 03 81 12 6d f0 00 00 a0 3a fa 05 00
+08 00 00 80 00 18 5c 28 10 02 00 0e 2a cc 72 00 08 01 00 00 00 42 00 00 00 e0 a5 00 08 28 08 c1 33 7b 01 00 11 44 0c 05 50 10
+00 00 00 00 99 83 e8 15 00 00 03 60 1e c0 50 00 00 00 00 00 00 12 00 00 57 c8 70 a0 00 08 1d da 21 30 00 20 9e 04 ee f7 00
+00 00 00 03 01 00 00 01 50 00 00 0c 00 20 05 08 00 00 00 00 00 00 00 24 3c 10 87 00 00 00 10 8b 20 49 00 00 10 63 ef bf f0 00
+00 00 01 02 00 0d 73 d5 00 0c 11 e7 e5 ca 00 08 00 50 00 00 00 00 01 c6 80 18 10 00 00 09 80 12 61 30 00 40 00 72 7c a7 02
+08 00 00 00 40 00 58 28 18 00 a0 80 34 c0 f5 00 00 00 00 00 00 00 00 02 01 cc 42 00 00 80 10 05 34 49 00 08 00 06 4e 0a 20 80
+00 00 30 00 90 0d aa 81 00 00 01 42 c0 c0 50 08 00 00 00 00 00 04 0b 62 5d ca 00 03 28 07 80 1a 2d f0 00 20 1c 7a 1e f5 00
+00 00 03 00 87 80 74 3e 90 00 00 24 2a c0 0a 00 00 00 00 00 00 40 a1 1c 68 10 a0 00 31 00 08 03 a3 7b 80 04 01 c4 1d 0f a2 10
+00 00 00 00 38 87 67 fc 00 00 00 00 56 e5 f4 00 00 00 00 00 00 00 06 00 1c ca 54 00 00 28 00 9a 2d d0 00 80 06 03 6e e2 41
+00 00 00 00 00 00 3b be c0 40 00 00 20 10 5f 60 00 00 00 00 00 40 00 04 34 59 6a 40 00 00 10 03 a3 7b 10 04 00 02 67 9c 35 84
+00 00 18 00 50 08 01 f9 08 60 01 e0 66 8c 10 01 80 00 00 00 00 70 0a 00 45 8f 50 01 90 00 0e 07 03 c0 0a 04 14 06 7e e2 40
+80 00 00 00 00 8b b0 2f 50 00 00 24 35 7f 42 08 00 00 00 00 00 80 01 04 3d c0 ff 01 02 80 01 00 00 3c 20 20 02 63 76 8c 34 00
+00 00 30 08 50 0f 71 41 00 c0 00 07 d5 85 00 00 00 00 00 00 00 00 00 00 01 a2 00 31 00 00 0e 00 00 00 00 00 00 23 6e e3 40
+00 00 03 00 00 80 98 29 50 0c 00 00 78 20 50 00 00 00 00 00 00 80 00 00 34 c0 10 00 30 30 00 00 00 00 00 00 00 04 1e d4 34 08
+00 02 00 00 00 07 b0 01 00 00 01 40 2d 8a 50 20 00 00 00 00 00 00 21 c0 05 8e 70 00 00 00 00 00 00 00 00 a0 46 71 cc a0 00
+00 00 00 00 0d 00 f2 42 10 00 00 2e 01 50 80 00 00 40 00 00 00 00 00 1c 00 ce d3 01 00 00 00 00 00 00 00 00 00 02 c5 0f a0 00
+00 00 00 00 00 03 e9 69 00 00 02 00 75 a0 20 00 00 00 00 00 00 00 00 47 4c bc 74 00 09 80 00 00 00 00 01 00 00 05 db e3 40
+00 00 20 00 03 0a 5b 56 d0 00 00 1c 36 6b 00 08 00 00 00 00 00 40 00 be 20 78 c2 40 01 00 00 00 00 00 00 00 00 42 3d c4 36 00
+00 00 00 00 51 80 79 54 00 00 00 00 0f fd 10 00 00 00 00 00 00 00 08 40 05 9f 70 00 10 00 00 00 00 00 00 00 20 07 5a f1 40
+00 00 00 00 09 00 f0 2a 80 00 00 3c 03 ca d2 00 00 00 f0 00 00 00 01 40 01 ca ff 00 00 80 d0 00 00 00 00 00 00 42 36 eb 06 04
+00 00 00 00 00 89 70 14 08 00 00 06 20 fd 10 00 08 00 00 00 00 00 01 c0 1c c4 50 20 00 20 00 00 00 00 00 c0 1c 77 6f e3 40
+00 00 00 00 00 0a 94 21 42 00 00 00 3d dc e2 00 01 01 00 00 00 40 00 0e 02 c0 85 00 00 04 00 00 00 00 00 0a 01 e3 76 d4 34 04
+00 00 30 00 50 07 b7 bc 00 40 01 c0 2d a5 20 23 00 00 00 00 00 40 00 00 3d 85 b0 33 00 01 00 00 00 00 04 0c 04 03 dc a0 00
+00 00 01 00 00 00 f2 3f c0 24 00 00 01 60 51 00 10 00 00 00 00 0c 00 00 00 d0 02 00 30 00 08 00 00 00 00 40 00 00 05 0f a0 00
+04 00 00 00 50 08 02 95 04 10 03 c6 ec b7 47 80 00 00 00 00 08 80 00 00 1e ef a0 00 10 55 00 5e 3d 40 00 40 00 64 cb 00 70
+c0 00 00 00 00 8b d0 16 90 11 00 44 2e 58 50 40 00 00 00 00 00 00 02 00 00 10 fa 00 03 00 58 b0 43 d4 00 00 00 02 3e b1 05 9c
+00 00 18 00 70 07 a3 e8 00 60 82 02 95 ac 00 00 00 00 00 00 00 a0 16 00 0c a8 10 20 10 05 1e 9a bd c0 02 46 26 05 4e e2 40
+40 00 00 00 00 80 04 3e 80 00 00 2c 33 fe fc 00 00 00 00 00 00 1a 0c bc 03 40 8b 10 02 80 08 b3 e3 fc 00 a0 00 43 97 cc 34 08
+00 00 00 00 00 01 3b fc 48 c0 01 42 ed a4 77 b0 00 00 00 00 00 00 01 40 5f a8 00 00 00 08 0b 5e 17 c0 00 20 04 79 4e e2 40
+40 00 00 00 00 00 34 3b c0 4c 00 3e 7a 50 42 41 00 00 00 00 00 40 00 00 20 1f 80 00 00 00 b1 d0 41 7c 00 04 03 e4 35 9c 34 00
+00 02 00 00 79 83 33 fc 40 10 00 02 c4 aa 00 00 00 00 00 00 00 00 a0 00 40 0f 04 00 00 00 00 3b 3d 40 80 40 00 75 5c e2 40
+00 00 00 00 00 80 14 3d 40 01 00 00 7f ca db 00 00 00 00 00 00 80 00 00 2c 00 f0 c0 40 00 d0 97 82 04 00 02 01 c7 57 9c 34 04
+00 00 04 00 80 11 63 d4 00 00 e5 40 05 89 10 20 00 00 00 00 00 00 02 00 37 ef 70 00 20 09 80 1b 3f c8 c0 0a 06 25 5b e2 40
+00 00 20 40 05 0a 00 3d 40 00 00 02 2c 50 e3 00 00 00 00 00 00 40 04 bc 03 ef ff 01 80 00 10 09 03 dc 40 00 00 06 f5 8c 34 18
+00 00 03 80 00 00 32 bc 00 00 00 00 26 ea 35 24 28 00 00 00 00 00 10 00 5d c0 50 80 00 07 01 13 bd c0 00 80 2e 01 4d e2 40
+00 00 00 18 00 00 10 17 c8 00 00 3c 03 60 03 48 41 00 00 00 00 40 01 00 20 30 05 00 00 00 00 ed 2b fc 02 04 01 c2 17 8c 36 00
+08 00 00 00 00 11 63 d4 00 02 03 47 dc cf 50 00 00 00 00 00 08 00 00 e2 1f af 52 00 00 09 01 5b a8 00 00 00 00 25 6c a0 00
+00 00 00 00 00 0a 90 3f c0 00 00 06 60 60 ff 00 00 00 00 00 00 00 40 02 3c d0 ff 08 00 02 70 f0 c2 80 04 00 00 07 01 0a 02 10
+00 00 10 00 58 00 03 fc 04 c0 14 00 04 c5 04 20 00 00 00 00 00 40 02 47 9d e2 00 20 00 20 00 00 00 00 0c 00 00 02 6e a4 42
+00 00 03 00 00 00 00 3f c8 14 00 80 02 70 10 40 80 00 00 00 00 44 00 3c 40 20 20 00 80 04 00 00 00 00 00 50 04 00 0f 0e 56 08
+40 00 00 00 91 81 22 80 00 00 00 00 40 aa f6 00 00 00 1e 80 00 00 04 06 01 af 04 00 00 00 08 72 21 30 00 00 00 01 5c 85 00
+00 00 20 00 09 0e 14 00 00 00 02 00 2b c0 5f 40 00 00 00 03 00 00 00 00 00 00 f0 40 00 00 01 03 00 49 0c 00 00 02 dc 9e a0 00
+00 00 18 00 80 09 b8 0d 0d 60 01 40 24 e0 00 01 80 00 00 80 00 60 02 60 00 cf 04 01 82 00 00 72 21 30 00 00 1f 03 6e c1 02
+08 00 00 00 01 00 30 28 d8 20 02 2c 00 7d 2e 00 00 00 00 05 00 01 00 04 00 00 f0 40 00 00 00 09 00 49 00 00 00 03 cf 0e 60 00
+68 04 30 00 80 c3 b9 09 25 c0 02 00 4d 8f 00 80 00 00 00 80 00 02 00 07 80 cf 04 0b 00 00 19 52 21 30 00 44 26 7b 7d a7 00
+03 82 83 00 0b 10 76 62 d2 04 00 3c 2a 60 aa 00 00 20 00 c5 00 00 00 00 40 00 f0 40 10 00 00 83 80 49 00 02 02 40 16 ea 20 00
+00 73 00 00 18 0d ab 04 00 00 04 07 20 82 00 00 04 0f 00 00 00 00 62 00 00 af 04 00 10 01 99 52 21 30 00 00 86 31 5c c5 02
+00 00 00 00 00 8c f7 f9 40 00 00 80 00 5d 10 00 00 00 d8 09 00 00 80 1c 00 10 f0 40 00 82 00 89 80 49 00 00 00 32 c1 0c 50 00
+00 00 00 00 00 07 6b e8 00 00 10 03 c6 aa 00 00 00 07 00 00 00 00 02 60 00 8f 04 00 00 50 01 7a 21 30 40 00 00 05 ce 03 02
+00 00 20 00 00 00 0c 3e 80 00 00 80 78 10 a0 00 00 00 08 0f 00 00 00 04 00 10 f0 40 00 02 90 f3 00 49 00 00 00 03 f5 05 31 00
+48 00 00 00 18 01 6b d4 00 00 08 00 00 85 00 00 80 00 00 00 00 00 e0 c0 41 8f 04 10 00 00 00 5a 21 30 00 06 04 28 00 00 00
+00 c0 00 00 01 00 10 3f c0 00 00 40 02 d0 a0 08 00 00 90 0d 00 00 00 02 20 00 f0 61 40 00 00 09 00 49 00 00 00 26 00 00 00 00
+00 00 00 00 18 f1 3c 08 00 00 00 e2 06 b1 f0 10 00 07 9e 00 00 00 00 03 01 8f 04 00 00 49 80 5a 21 30 01 0c 04 26 f8 f5 00
+08 02 c0 00 00 08 16 85 40 00 00 00 43 d8 1a 00 00 00 08 07 00 00 00 00 40 00 f0 41 00 02 10 03 80 49 00 10 00 63 ee 0a 00 00
+40 00 30 00 00 00 39 6d 08 c0 03 c0 3d a8 10 23 00 01 00 00 00 c0 c1 c0 7d da 50 03 00 09 00 5a 21 12 40 00 14 00 d8 42 02
+00 00 01 00 00 09 50 29 d0 4c 00 04 01 d0 d1 00 30 00 08 03 00 0c 00 02 3b fc 85 01 30 04 10 09 80 49 05 00 00 00 1f 0e 21 00
+00 00
+11 01
+01 01
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 04 00 00 00 00 00 00 10 00 00 00 00 00 01 c0 00 00 00 00 10 00 00 00 00 00 00 40 00 00 00 00 0d 01 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 81 00 00 00 00
+00 00 00 00 00 04 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 80 30 00 00 00 00 00 00 40 80 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 02 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 10 00 00 8b c3 d5 00 40 00 02 06 0f 54 00 00 00 00 00 00 40 00 02 25 0a 04 03 00 00 08 38 3d 52 0c 00 00 22 d0 a0 40
+00 00 03 00 00 19 fe a8 10 04 00 00 05 7a a0 40 00 00 00 00 00 0c 00 06 04 5a f5 40 30 00 10 15 ba 81 00 40 00 60 47 af 54 00
+00 00 00 00 00 85 c1 41 00 00 00 42 35 d7 24 00 00 00 00 00 00 00 00 02 2f 0f 54 00 00 00 08 b8 3d 50 00 00 00 61 f0 50 48
+00 00 00 00 01 81 bf bd 50 00 00 26 06 1e b1 40 02 80 00 00 00 00 00 06 07 ee a0 40 00 00 18 1f ea 81 00 00 00 67 ed ef 54 00
+00 00 00 00 11 05 35 c9 00 00 00 00 16 05 04 00 00 00 00 00 00 00 02 c2 e5 0f 54 00 00 00 0f 30 3d 50 00 00 00 01 60 50 40
+00 00 00 00 00 0e 86 ec 50 00 00 00 02 da f5 40 00 38 00 00 00 00 00 20 01 ec a0 40 00 00 01 a5 e2 81 00 00 00 00 3d ef 54 00
+00 02 00 00 00 0d 81 41 00 00 00 02 b4 d7 24 00 20 00 00 00 00 00 00 02 9e 05 04 00 00 00 80 b0 3d 50 00 00 00 43 60 50 40
+00 00 00 00 00 00 3e bd 50 00 00 00 00 1a b1 40 00 00 00 00 00 00 00 00 04 5a f5 40 00 00 70 0d aa 81 00 00 00 07 8d af 54 00
+00 00 00 00 10 0f 75 c9 00 00 00 40 3f 05 04 00 00 00 00 00 00 00 00 40 27 0f 54 00 00 01 00 fc 14 10 00 00 04 03 f0 50 40
+00 00 20 00 09 8a 02 ec 50 00 00 26 60 ca f5 40 00 00 00 00 00 00 00 20 61 5a a0 40 00 00 99 8b ab d5 00 00 03 c6 1c af 54 00
+00 00 00 00 00 07 c1 41 00 00 01 40 1d d7 24 00 00 00 00 00 00 00 00 40 2d 0a 04 00 00 01 00 d4 14 10 00 00 04 01 f0 50 40
+00 00 00 00 00 18 3b bd 50 00 00 20 60 0e b1 40 00 00 00 00 00 00 00 20 62 4e f5 40 00 00 81 83 3b d5 00 00 02 06 0c ef 54 00
+00 00 00 00 00 0b 43 d5 00 00 02 c0 1e 05 04 00 00 00 00 00 00 00 00 00 3e 05 04 00 00 05 00 30 3d 50 00 00 1c 01 60 50 40
+00 00 01 80 00 00 f7 a8 10 06 00 20 00 ce f5 40 00 00 00 00 00 06 00 00 02 ee f5 40 1a 00 80 07 6a 81 00 60 00 02 9c ef 54 00
+00 00 00 00 10 01 c3 e9 00 00 00 00 2c 0f 54 00 00 00 00 00 00 00 01 44 1e 05 04 00 00 00 00 b4 3d 50 00 00 00 03 d0 f5 40
+00 00 00 00 08 00 3f a8 10 00 00 04 01 ca a0 40 00 10 00 00 00 00 00 14 7a 4a f5 40 00 00 00 0d 72 81 00 00 00 00 15 8a 04 00
+00 00 10 00 00 8f c1 41 00 40 00 02 3d 05 04 00 00 00 00 00 00 40 00 02 06 0f 54 01 00 0f 08 78 3d 50 0c 00 02 23 f0 50 40
+00 00 01 00 00 01 b6 bd 50 04 00 00 06 fa f5 40 00 38 00 00 00 0c 00 00 65 fa a0 40 10 00 00 1d ea 81 00 c0 01 c0 7d af 54 00
+00 00 00 00 00 85 75 c9 00 00 00 02 15 c7 24 00 00 00 00 00 00 00 00 42 16 0f 54 00 00 00 08 38 3d 50 00 00 00 61 70 50 40
+00 00 00 00 01 81 87 ac 50 00 00 06 06 1f b1 40 00 00 00 00 00 00 00 26 65 ea a0 40 00 00 18 1f ea 81 00 00 00 67 e7 ef 54 00
+00 00 00 00 00 05 81 41 00 00 02 40 14 05 04 00 00 00 00 00 00 00 03 c0 3c 05 04 00 00 0f 00 f0 14 10 00 00 00 01 60 50 40
+00 00 00 00 00 00 b6 bd 50 00 00 00 02 fa f5 40 00 00 00 00 00 00 00 00 01 5a f5 40 00 00 00 0b eb d5 00 00 00 00 3d af 54 00
+00 02 00 00 00 ed 35 c9 00 00 00 00 34 c7 24 00 38 00 00 00 00 00 00 00 3c a7 24 00 00 00 00 d0 14 10 00 00 00 03 60 50 40
+00 00 00 00 00 01 06 ac 50 00 00 00 00 1b b1 40 01 00 00 00 00 00 00 00 00 1a b1 40 00 00 58 07 eb d5 00 00 00 00 07 af 54 00
+00 00 00 00 10 0f c1 41 00 00 00 42 fd 05 04 00 00 00 00 00 00 00 00 40 07 0f 54 00 00 01 00 f4 14 10 00 00 04 03 f0 50 40
+00 00 20 00 09 98 32 bd 50 00 00 26 60 ea f5 40 00 00 00 00 00 00 00 26 29 ca a0 40 00 00 99 83 ab d5 00 00 02 66 1c af 54 00
+00 00 00 00 10 07 75 c9 00 00 00 40 1d c7 24 00 00 00 00 00 00 00 13 64 16 0f 54 00 00 01 00 38 3d 50 01 0a 06 03 d0 a0 40
+00 00 00 00 08 18 03 ac 50 00 0c 20 60 0f b1 40 04 10 00 00 00 00 00 9e 29 da a0 40 00 00 81 8f 32 81 00 10 02 e6 04 ef 54 00
+00 00 00 00 90 05 81 41 00 00 00 c0 14 05 04 00 00 00 00 00 00 00 60 00 05 0f 54 00 00 07 0a 74 3d 50 00 00 14 42 40 f5 40
+00 00 01 80 00 0a 33 bd 50 06 00 20 00 ee f5 40 00 00 00 00 00 06 00 00 01 da a0 40 18 00 80 0f 62 81 00 60 00 06 bd 8a 04 00
+00 00 00 00 00 0b 43 d5 00 00 00 00 0d 0f 54 00 04 00 00 00 00 00 00 20 15 0f 54 00 00 00 00 d0 14 12 00 00 00 01 60 50 40
+00 00 00 00 00 00 56 28 10 00 00 00 03 5a a0 40 02 80 00 00 00 00 00 1e 01 de a0 40 00 00 00 0f ab d5 00 00 01 60 06 af 54 00
+00 00 10 00 01 8f c1 41 00 40 00 02 0e 0f 54 00 00 00 00 00 00 40 00 02 3d 05 04 03 00 00 8d 07 2c 58 0c 00 1c 21 e0 f5 40
+00 00 03 00 00 0f fe bd 50 0c 00 00 05 78 a0 40 00 00 00 00 00 0c 00 00 06 7a f5 40 30 00 10 11 ea c5 00 40 02 60 77 ca 04 00
+00 00 00 00 00 85 7d c9 00 00 00 02 15 f7 24 00 00 00 00 00 00 00 00 02 01 8b 14 00 00 05 00 b4 3e 90 00 00 00 61 d0 50 40
+00 00 00 00 01 81 87 ec 50 00 00 06 26 1e b1 40 00 00 00 00 00 00 00 06 04 ff b1 40 00 00 18 0b 7a 81 00 00 00 03 ff ef 54 10
+00 00 00 00 98 05 81 41 00 00 02 c0 16 05 04 00 00 00 00 00 00 00 02 c0 14 c4 24 00 00 00 18 30 3d 50 00 00 2c 01 d0 f5 40
+00 00 00 00 05 80 fe bd 50 00 00 20 03 da f5 40 00 38 00 00 00 00 00 26 02 61 e7 40 00 00 00 b5 ba 81 00 00 02 00 3e 8a 04 00
+00 02 00 00 00 0d 3d c9 00 00 00 20 34 f7 24 00 00 00 00 00 00 00 00 00 34 87 24 00 00 00 88 10 3e 90 00 00 00 23 c0 50 40
+00 00 00 00 00 00 06 ec 50 00 00 04 00 1a b1 40 00 00 00 00 00 00 00 00 00 1b b1 40 00 00 71 bb 3a 81 00 00 00 00 1f af 54 00
+00 00 00 00 10 0f c1 41 00 00 00 40 3f 05 04 00 00 00 00 00 00 00 00 40 3d 05 04 00 00 00 00 38 3d 50 00 00 06 03 f0 50 40
+00 00 20 00 09 98 7a bd 50 00 00 26 61 ca f5 40 00 00 00 00 00 00 00 20 60 6a f5 40 00 00 19 85 6a 81 00 00 02 46 06 af 54 00
+00 00 00 00 10 07 7d c9 00 00 00 40 1d f7 24 00 00 00 00 00 00 00 00 40 1d 87 24 00 00 00 00 b4 3e 90 00 40 04 02 50 a0 40
+00 00 00 00 08 18 03 ec 50 00 00 21 60 0e b1 40 00 00 00 00 00 00 00 20 60 0f b1 40 00 00 01 83 32 81 00 02 16 06 34 ef 54 00
+00 00 00 00 00 05 81 41 00 00 01 44 16 05 04 00 00 00 00 00 00 00 01 c4 14 05 04 00 00 01 00 03 2c 50 00 40 2c 24 00 f0 40
+00 00 01 80 00 00 7b bd 50 06 00 00 29 ce f5 40 00 00 00 00 00 06 00 20 60 6e f5 40 18 00 80 01 3a c5 00 62 02 02 81 8f 04 00
+00 00 00 00 00 0f 03 d5 00 00 00 00 3d 0f 54 00 28 00 00 00 00 00 00 00 05 0f 54 00 00 01 00 14 3e 90 00 00 00 2a d0 f5 40
+00 00 00 00 00 00 d7 28 10 00 00 00 01 58 a0 40 01 10 00 00 00 00 00 00 03 58 a0 40 00 00 80 03 72 81 00 00 00 60 45 8a 04 00
+00 00 10 00 00 8f c1 41 20 40 02 42 1f 0f 56 00 00 00 00 00 00 c0 00 03 b7 05 04 01 00 00 08 fc 14 10 04 00 00 3c c0 5a 40
+00 00 03 00 00 01 b6 bd 50 0c 00 00 07 78 a0 40 00 00 00 00 00 0c 00 0e 06 5a f5 60 30 00 00 1b 6b d5 00 c0 00 00 00 0a 54 00
+00 00 00 00 00 85 75 c9 00 00 00 02 07 0f 54 00 00 00 00 00 00 00 80 02 15 97 24 00 00 00 08 57 5c 90 00 00 00 00 10 0f 40
+00 00 00 00 01 81 87 ac 50 00 00 06 05 f8 a0 40 00 00 00 00 00 00 00 06 06 1a b1 40 00 00 00 18 7a c5 00 00 00 06 01 e0 f4 00
+00 00 00 00 70 05 81 41 00 00 00 c0 3c e7 24 00 00 00 00 00 00 00 00 00 16 05 04 00 00 07 00 58 14 10 00 00 36 24 7a 82 00
+00 00 00 00 0d 80 b6 bd 50 00 00 00 02 1b b1 40 00 00 00 00 00 00 00 00 02 5a f5 40 00 00 80 0b 6b d5 00 00 07 60 1d b8 10 08
+00 02 08 00 00 cd 35 c9 00 20 00 00 34 05 04 00 00 00 00 00 00 20 00 00 34 97 24 00 80 07 0c d3 5c 90 02 01 00 27 40 80 00
+00 00 00 00 00 01 06 ac 50 00 00 00 01 fa f5 40 00 00 00 00 00 00 00 00 00 1a b1 40 00 00 00 10 6a c5 00 00 08 e0 25 ae 00 a0
+00 00 00 00 10 0f c1 41 00 00 60 43 fd e7 24 00 00 00 00 00 00 00 00 42 3f 05 04 00 00 01 00 fc 14 10 00 00 2c 40 30 d1 00
+00 00 00 00 09 98 32 bd 50 00 00 26 60 0b b1 40 00 00 00 00 00 00 00 26 64 4a f5 40 00 00 99 83 2b d5 00 00 00 03 0c ad 10 20
+80 00 00 00 10 07 75 c9 00 00 00 40 1d 05 04 00 00 00 00 00 00 00 00 40 1d 97 24 00 00 01 00 07 6c 50 00 00 24 3a e0 aa 00
+00 00 00 00 08 18 03 ac 50 00 00 20 61 ee f5 40 00 00 00 00 00 00 00 20 60 0e b1 40 00 00 81 81 3a c5 00 00 00 07 35 ca 50 00
+80 01 00 00 00 05 81 41 00 00 c0 06 d4 e7 24 00 00 00 00 00 00 00 00 00 16 05 04 00 00 00 00 18 3e 90 00 00 00 20 d0 ca 00
+08 00 01 80 00 00 33 bd 50 06 02 00 28 0f b1 40 00 00 00 00 00 06 00 01 00 4e f5 40 18 00 00 03 32 81 00 60 20 00 40 cc 50 20
+10 02 00 00 00 0b 02 81 00 00 80 40 05 0f 54 00 04 00 00 00 00 10 40 02 25 0f 54 00 00 00 10 b4 3d 50 10 00 00 02 40 55 00
+00 00 00 00 00 00 3a bd 50 00 00 00 01 5a a0 40 00 00 00 00 00 00 00 00 01 5e a0 40 00 00 18 a5 22 81 00 80 00 60 35 ca 50 00
+00 00 00 00 00 00 00 00 00 40 00 00 05 00 f0 00 00 00 00 00 00 c0 00 40 26 01 24 02 00 00 00 56 3f c2 08 00 00 00 1c 03 00
+00 00 00 00 05 80 00 00 00 04 00 00 03 68 f0 00 00 00 00 00 00 0c 00 3c 01 68 84 40 30 00 58 0d f3 dc 00 c0 00 00 00 80 30 00
+00 00 00 00 00 00 00 00 00 00 00 04 36 d0 c4 00 00 00 00 00 00 0a 20 40 16 01 24 00 80 50 88 b4 15 70 02 00 00 02 40 ff 00
+00 00 00 00 00 00 00 00 00 00 00 00 22 fa 5e 40 00 00 00 00 00 00 40 26 01 68 84 c0 00 02 11 b9 ea 95 02 00 00 07 0c 0a 50 00
+00 00 00 00 00 a1 2f 30 00 00 00 00 2f 00 c4 00 00 00 00 00 00 00 00 00 24 01 24 00 00 00 08 1b d6 10 00 00 00 01 4c 42 00
+00 00 00 00 00 01 da 0c c0 10 00 00 63 78 5e 42 00 00 00 00 00 00 00 00 01 68 84 c0 00 00 00 1f 3b 95 00 00 00 00 00 08 10 00
+00 02 08 00 50 00 aa 58 80 20 00 02 07 0a 10 00 00 00 00 00 00 20 00 03 14 01 24 00 80 00 80 d8 3e b0 02 00 00 00 00 f5 01
+00 00 00 00 0d 80 52 65 a0 00 00 00 04 7c a0 02 00 00 00 00 00 00 00 00 05 e8 84 c0 00 00 70 05 ea 81 00 00 00 60 14 cf 50 00
+00 00 00 00 78 00 2e 58 00 00 00 40 05 04 00 00 00 00 00 00 00 20 00 02 26 01 24 00 80 01 09 fb 2d 50 02 00 00 01 4c 7e 00
+00 00 20 00 01 80 7a 25 a0 00 00 34 28 18 80 02 00 00 00 00 00 00 00 06 65 48 84 c0 00 00 d0 15 3a 85 00 00 00 00 00 0b d0 00
+00 00 00 00 b0 d0 36 58 00 c0 00 02 95 00 f0 00 00 00 00 00 00 40 00 02 16 01 24 00 00 01 00 94 2a b0 0c 00 04 00 00 fa 10
+00 00 00 00 08 01 5a 65 a0 0c 00 16 06 4a f0 00 00 00 00 00 00 0c 06 1e 65 48 84 c0 00 00 99 88 39 55 00 c0 02 00 14 cf a0 00
+00 00 00 00 08 00 b2 58 00 00 02 e2 95 d0 c4 00 00 00 00 00 00 00 00 00 24 01 24 02 00 00 09 58 3e b0 18 00 00 00 1c cc c0
+00 00 00 00 01 80 12 25 a0 06 00 3e 2b 5b 5e 40 00 00 00 00 00 06 00 00 01 c8 84 c0 2a 80 18 11 aa 81 20 a0 00 00 00 8c c4 00
+00 00 00 00 00 e0 32 58 00 00 01 42 b6 8f 34 00 00 00 00 00 00 00 00 00 14 01 24 20 00 00 00 7b 9c 10 00 00 00 2a 40 f0 c0
+01 00 00 00 00 00 50 25 a0 00 00 36 02 fa fb 40 00 00 00 00 00 00 00 00 71 c8 84 c0 00 00 18 07 2f c5 00 00 00 00 4c aa a4 00
+00 00 10 00 00 d0 01 2c 00 40 03 47 7d e4 40 00 00 00 00 00 00 08 00 02 97 ff 70 02 00 01 0a 13 ff c2 08 00 00 00 10 e4 c8
+04 00 03 00 05 9d 7e 92 c0 84 00 06 66 49 bb 00 00 00 00 00 00 00 00 00 03 d9 e7 00 b0 00 80 a1 eb dc 02 c0 00 00 05 ae 46 20
+86 00 00 00 00 81 43 c0 00 00 00 24 1e 00 f0 00 00 00 00 00 00 00 00 03 e5 0f f0 00 00 45 08 58 3f ca 00 40 00 2d 6e ff 00
+00 00 00 00 01 81 5b 83 c0 00 0c 14 7a cc f0 00 00 00 00 00 00 00 00 00 6b 7c f7 02 80 02 00 1f 6b d4 04 00 00 60 1c af 70 10
+08 00 00 00 10 ff 82 70 00 00 00 00 3d e4 44 04 28 00 00 00 00 00 08 02 83 0f 50 00 00 00 0a 78 3e b0 00 00 02 00 30 a0 01
+04 00 00 00 00 00 7e 24 c0 80 00 06 02 49 bb 40 01 00 00 00 00 00 00 40 68 ee f5 00 00 00 00 1b ea 81 00 00 00 e0 16 8a 00 00
+00 02 00 08 00 01 40 28 00 00 10 06 c5 07 70 00 00 00 00 00 00 00 14 07 bd af f0 00 08 05 00 d7 29 90 00 00 00 3a 4b 81 00
+00 00 00 00 00 00 5b a8 00 00 00 06 28 f8 44 02 00 00 00 00 00 00 20 94 24 de f7 00 01 00 18 0f fa 95 00 00 00 00 40 14 20 00
+00 00 00 00 b0 0f 03 e8 00 00 00 00 17 05 a0 00 00 00 00 00 00 00 09 c6 f5 0f a0 00 00 0d 10 3c 3e b0 20 00 3c 23 50 50 c0
+00 00 10 00 09 80 06 3d 40 00 00 00 69 fa f0 00 00 00 00 00 00 00 01 20 2a 1a f5 00 00 00 1d e3 aa 81 00 00 02 06 60 aa 04 00
+00 00 00 00 10 07 75 c9 00 c0 00 40 01 0e 10 00 00 00 00 00 00 00 00 26 c5 00 20 80 00 41 00 b4 3d 70 00 40 b6 02 e0 fa d0
+00 00 00 00 08 00 03 ec 50 0c 00 20 01 4c e1 02 00 00 00 00 00 00 00 1c 2a 68 00 02 00 02 81 8d 3a 81 00 00 01 46 17 aa 06 00
+80 00 00 02 b0 05 81 41 00 00 02 c0 01 0e 10 00 00 00 00 00 00 00 03 63 c5 b0 00 02 00 2d 00 3f 9e 90 08 02 00 00 0b fc 40
+00 00 01 80 18 00 3b bd 50 06 00 20 29 cc e1 02 00 00 00 00 00 00 00 14 00 4a 80 00 28 00 00 07 ea 85 00 a0 00 00 00 ff c4 00
+00 43 01 05 50 0b 43 d5 00 00 10 42 00 00 f0 00 04 00 00 00 00 00 03 62 67 df f0 00 00 00 80 d3 d9 50 00 40 04 20 5d 4a 40
+00 07 00 28 00 00 d7 a8 10 00 00 a0 04 1c 0f 00 00 00 00 00 00 00 00 14 60 ce f7 00 00 04 50 0d 3e 95 00 00 02 00 4e ee 54 00
+00 00 30 00 10 a9 a0 78 00 c0 00 03 7f 86 30 00 00 00 00 00 00 40 00 00 15 8c 30 03 00 07 80 f6 b9 40 04 00 00 03 e9 20 00
+40 00 01 00 0d 81 3e 74 80 0c 00 1c 04 4c 6c 00 00 00 00 00 00 0c 00 1e 03 c8 e3 11 b0 00 90 a1 7a 3c 00 40 00 00 1c d0 00 00
+00 00 00 00 00 07 e0 b9 00 00 14 02 56 d7 80 00 00 00 00 00 00 00 00 20 04 ff f0 00 00 50 0a fb 3f c0 40 00 00 74 c0 1a 01
+00 00 00 00 01 9a fb 09 50 40 00 80 06 7d 4b 00 00 00 00 00 00 00 00 1e 78 5c f7 00 81 02 00 ef 2f dc 00 00 00 07 81 a2 50 20
+00 00 00 00 50 8b 82 91 00 00 00 22 c7 0c c0 00 00 00 00 00 00 00 02 e0 05 00 00 14 00 0d 0d 50 36 c0 00 02 80 03 f0 ff 08
+00 00 00 00 05 81 77 9d 10 c0 00 14 03 48 33 02 00 00 00 00 00 00 00 2e 01 c8 40 02 00 00 00 b0 03 9c 08 00 10 60 1c aa f0 00
+00 02 04 00 00 ab 80 0c 80 00 00 02 ed 05 f0 00 08 00 00 00 00 02 14 02 04 ff 00 00 00 0f 00 f7 3b c0 00 00 1c 02 4b 1a 08
+00 00 00 40 00 19 7f 10 c0 80 00 01 00 4e 50 00 01 00 00 00 00 00 40 80 04 ea e0 00 00 00 80 09 3f fc 00 00 0a 00 2f b1 50 00
+00 00 00 00 08 09 02 a8 00 00 00 20 0e 00 f0 00 00 00 00 00 00 00 00 40 00 0f 04 00 00 01 00 f4 3d 40 00 01 1c 03 40 5a c0
+00 00 20 00 01 00 77 0c c0 80 00 14 02 fa f0 00 00 00 00 00 00 00 00 20 00 1e f0 40 00 10 80 03 fa 94 00 20 0a 03 80 0a 54 00
+00 00 14 00 00 01 74 3c 00 00 00 04 2c 0a f0 00 00 00 00 00 00 00 03 c0 34 05 a4 03 00 5d 00 90 36 c8 00 00 04 3c 10 f0 c0
+00 00 01 40 00 00 9a 61 c0 00 20 00 2a 7c fa 00 00 00 00 00 00 00 0c 20 60 00 a5 40 30 02 81 85 fb dc 08 00 22 03 00 8f 04 00
+00 00 00 00 d0 b7 37 29 04 00 00 20 00 00 f6 00 00 00 00 00 00 00 00 00 03 05 05 08 00 00 00 f3 40 00 00 00 24 7d 60 50 00
+80 00 01 80 01 80 87 b1 50 06 00 14 68 1a 0f 40 00 00 00 00 00 06 00 00 00 ca 50 40 18 00 00 03 b4 40 00 60 00 03 c4 c0 00 00
+04 00 02 80 71 e7 a7 f9 04 00 00 02 81 0f 04 00 00 00 00 00 00 00 03 42 c0 0f 54 00 00 05 8f b2 e8 10 40 00 00 22 40 5a c0
+00 00 00 13 08 0a b7 ff 10 08 08 16 04 1e f0 40 00 00 00 00 00 00 00 06 00 5a f5 61 00 20 78 09 7a 01 00 00 e0 06 40 0a 54 00
+00 00 10 00 00 8f 41 41 00 40 00 43 c0 00 00 00 00 50 00 00 00 40 10 03 04 0a 52 00 10 2b 99 10 39 80 04 00 00 30 cc d0 00
+04 00 03 00 00 01 b6 bd 50 2c 00 20 04 00 00 00 00 00 00 00 00 04 00 86 04 00 5a 00 00 00 f1 d9 7b 04 08 c0 00 00 65 8d 50 00
+80 30 00 00 50 85 71 c9 00 00 00 40 00 00 00 00 00 00 00 00 00 80 00 04 25 05 20 10 00 00 1d 52 74 00 00 00 04 60 48 98 00
+00 00 40 00 00 01 87 ac 50 00 00 20 78 00 00 00 00 00 00 00 00 0c 20 00 22 e8 d1 02 00 00 00 c9 b7 54 00 00 02 02 7d ae 50 00
+0c 2c 00 00 90 e5 01 41 00 00 0a c3 fe 81 24 10 00 00 00 00 00 00 01 c0 35 05 a0 00 00 00 19 7c 00 00 00 01 0c 30 eb 6a 40
+04 40 40 00 00 01 b6 bd 50 00 00 40 07 69 84 40 00 00 00 00 00 00 00 14 33 4c ad 02 00 00 01 8b b1 00 88 00 0c 60 66 ba 55 00
+00 02 00 00 30 0d 31 c9 00 00 00 00 24 bd e4 00 00 00 00 00 00 00 00 43 ff 0f f0 00 00 00 8c f8 09 00 00 00 3c 03 da a6 40
+00 00 00 00 08 00 06 ac 50 00 00 06 03 e9 b7 e0 00 28 00 00 00 00 00 36 01 fa af 00 00 00 10 1f a9 40 08 00 c0 60 17 aa 54 04
+00 00 00 00 10 0f 41 41 00 0c 03 c0 3e 91 24 00 00 00 00 00 00 00 00 02 84 00 50 10 00 00 90 94 3e 82 00 40 04 3b 79 c3 40
+80 00 10 00 09 98 32 bd 50 00 60 20 3b 68 84 e0 00 00 00 00 00 00 0e 00 00 1c 0a 00 00 00 78 a8 3b d4 00 00 02 00 4f 8e 24 00
+30 00 00 00 10 03 42 81 04 c0 00 00 2c b1 24 00 00 00 00 00 00 40 00 00 1d 81 d0 10 28 01 00 f4 3e 80 00 a0 02 27 fd 70 48
+02 00 00 00 08 18 53 bd 50 0c 40 00 03 69 84 e0 00 00 00 00 00 04 00 1e 02 da 25 00 01 00 80 08 63 d4 80 04 01 c0 14 ff 14 00
+80 00 00 00 00 00 43 c1 00 00 00 03 7e c1 24 10 00 00 00 00 00 04 03 c3 0c 9a 10 00 00 00 9f 9f ff c0 02 01 24 34 df da 48
+00 00 05 80 00 00 02 3c 10 06 00 06 07 68 84 c0 00 00 00 00 00 06 00 20 26 df e2 10 00 00 10 9f 6b dc 00 a4 08 00 55 9a 44 00
+10 02 00 00 00 db 03 d5 00 00 20 00 2c 01 24 a0 04 00 00 00 00 00 03 67 cd c9 04 00 00 0f 00 fc 00 00 00 01 06 03 cd 70 40
+00 00 00 00 01 81 93 a8 10 00 00 00 63 c8 84 c0 00 00 00 00 00 00 00 14 3e 1c 60 40 40 00 00 01 f1 40 00 00 09 c6 04 ff 14 10
+00 00
+11 02
+01 01
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 08 00 00 00 00 00 00 20 00 00 00 00 08 0b 00 00 00 00 00 20 00 00 00 00 00 00 80 00 00 00 00 00 02 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 08 00 00 00 00 00 40 20 00 00 00 00 08 00 00 00 00 00 00 20 00 00 00 00 00 00 80 00 00 00 00 00 03 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 16 91 68 07 90 00 40 00 1f c1 80 00 00 00 00 00 00 08 02 29 fc 90 09 00 00 00 8d 3b ac 3e 85 00 0c 0c 68 00 b0 00
+00 00 00 c0 69 04 00 6e 00 00 00 04 f9 d0 00 00 00 00 00 00 08 00 13 fd ed 00 b8 00 00 00 cd 68 c2 90 20 02 83 04 00 18 00 00
+00 00 00 12 11 70 c0 00 00 00 00 2d c0 08 0d 00 00 00 00 00 00 00 02 ee f8 00 00 00 44 47 5f 80 30 00 04 05 a4 4a 00 b0 01
+00 00 00 00 48 02 f4 00 00 02 51 01 f9 01 00 00 00 00 00 00 00 00 00 af 5d 70 04 00 00 04 70 40 00 00 00 00 1a 41 00 1f 00 14
+00 00 00 12 11 38 00 00 00 40 00 1e 80 00 08 00 00 00 00 00 00 10 32 cd a0 00 00 00 00 af 16 9c 3c 00 00 05 a6 40 38 e0 00
+00 00 00 00 48 07 bc 00 00 02 40 14 3d 00 00 00 00 00 00 00 00 00 01 18 1f 00 00 00 00 00 50 a0 23 03 00 00 1a 4c 07 80 00 00
+00 02 00 16 91 22 03 00 20 00 28 0c 80 00 00 00 00 00 00 00 00 11 39 c7 e8 1c 20 00 00 0a 10 00 64 02 00 05 a4 5c 00 c0 00
+00 00 00 80 69 00 0c 2c 04 00 02 80 10 10 b0 00 00 00 00 00 00 00 03 7c 38 71 00 00 01 00 50 a0 03 00 50 00 1a 41 00 10 00 00
+00 00 10 12 11 75 40 40 40 30 30 57 b0 00 00 00 00 00 00 00 00 00 0c 2f d0 0e 00 00 10 0a 12 20 00 00 00 15 a4 4a 00 00 00
+00 00 20 00 48 06 42 30 02 00 02 00 1d f0 a0 00 00 00 00 00 00 00 00 91 6c 01 00 00 00 00 a0 82 c2 80 00 00 1a 41 00 00 00 00
+00 00 08 12 11 7c c0 08 00 80 0a 2c 91 80 00 00 00 00 00 00 00 11 00 86 81 08 00 20 00 af 33 00 28 00 00 15 a4 5a 00 80 00
+00 00 00 00 48 06 40 2c 00 00 00 30 5a 10 01 00 00 00 00 00 00 00 02 84 28 11 80 00 00 00 50 78 00 00 00 00 1a 40 05 1a 00 00
+00 00 00 02 11 34 03 80 00 02 03 f6 e0 00 00 00 00 00 00 00 00 00 00 00 09 00 a0 00 00 af 1a b4 00 00 04 05 a4 5e 00 0a 00
+00 00 00 00 48 07 b4 00 00 00 00 0e c9 00 a0 00 00 00 00 00 00 00 00 00 00 38 04 00 00 00 50 20 42 00 00 00 1a 40 00 00 00 00
+00 00 00 0e f3 6c e7 80 00 02 09 20 10 00 14 00 00 00 00 00 40 02 02 8f b1 9c 80 01 40 8b 72 84 00 00 82 05 a6 40 00 10 08
+80 00 00 00 ff 12 82 00 00 00 00 63 4a 30 c0 00 00 00 00 00 00 00 00 3c 0d 00 00 00 00 00 a0 e0 20 00 08 00 1a 4c 00 0e 0d 48
+00 00 10 2e 79 60 60 10 00 00 1d 3d d0 19 04 00 00 00 00 08 00 00 a3 f5 c0 18 00 00 02 c3 1f 60 3c 00 00 01 a4 5a 01 c1 00
+00 00 00 8e 8b fa 7c 02 00 00 02 b1 c8 01 c0 00 00 00 00 00 40 00 08 38 ce 00 00 08 08 a4 71 bb c2 00 20 88 1a 41 00 00 18 00
+00 00 00 00 a7 70 06 40 25 10 20 07 b0 01 00 10 00 00 00 10 20 41 91 c7 d8 00 00 11 02 8b d3 4e 04 00 d0 00 02 7d 01 e0 00
+00 00 00 00 0a 08 00 34 04 40 03 80 2a d0 80 01 40 00 00 00 84 00 0b 0c 6c b0 90 01 48 20 f9 30 23 c7 05 00 01 05 ef 0b 00 00
+00 00 04 08 15 75 c0 40 42 31 a1 55 e9 0e 00 0c 00 00 00 00 00 00 a9 4e af 1f 20 00 82 a5 3a 06 60 00 00 00 00 00 70 00 00
+00 00 00 00 85 03 40 20 02 34 0b 7c 38 91 00 00 c0 00 00 00 02 00 0a a8 3e 10 80 00 20 25 93 34 00 06 00 00 00 00 00 08 18 00
+00 02 04 04 77 6d 40 00 20 00 a2 ee 18 19 00 00 00 00 00 00 50 40 a9 55 e0 01 00 80 02 a5 12 c6 60 41 00 03 95 d8 78 04 00
+00 00 00 00 85 0f c0 00 41 0a 09 1f 6f 90 f0 00 00 00 00 00 52 00 0a a8 3b 00 80 04 00 2a a0 e4 00 07 20 04 11 5f 83 00 80 00
+00 00 00 08 05 7c 00 00 00 61 a9 de 80 01 00 00 00 00 00 06 00 00 a9 4f b0 1e 00 00 02 a5 7b a6 00 00 08 00 15 ca 00 00 04
+00 00 20 80 00 77 80 38 00 08 09 5e ee 70 a0 00 00 00 00 00 00 00 0a a8 29 00 00 00 00 aa a0 6c 63 80 00 00 17 c2 b0 00 10 80
+00 00 04 0a 03 f4 67 08 08 00 00 07 e0 1e 08 00 00 00 00 00 00 01 b9 c0 b7 09 41 40 02 a5 16 80 38 00 00 03 c1 60 00 00 00
+00 00 00 00 80 0b 74 71 00 00 00 14 0e 70 80 00 00 00 00 01 00 00 09 09 da 18 c2 08 00 2a a0 a0 02 00 00 00 3c 02 09 00 00 00
+00 00 00 0e fd 74 e7 80 00 00 04 7d b1 09 00 00 00 00 00 28 00 80 ab de c0 08 00 00 02 af 37 e0 38 00 00 00 00 00 b0 10 00
+00 00 00 00 fb 33 40 00 00 00 00 09 c8 00 a0 00 00 00 00 00 00 00 08 28 2e 00 e0 04 00 a0 a0 aa 40 20 00 00 00 00 03 8e 00 00
+00 00 00 04 27 7f c7 84 02 10 00 d4 d8 09 00 8c 00 00 00 30 50 01 2a cf fb 88 00 09 02 af 5e 40 70 00 30 00 00 00 00 00 00
+00 00 00 00 c1 53 ee 00 00 20 13 cc 2e f1 d0 00 80 00 00 02 00 00 00 2d 6f 18 00 00 80 20 a0 a4 02 00 02 00 00 00 00 08 00 00
+00 00 01 00 1f 7d 60 10 00 80 3b d7 d5 00 00 00 00 00 00 00 00 41 a3 d5 93 88 60 00 12 47 76 60 6e 02 00 18 80 cf 00 00 80
+00 00 00 00 10 da 00 2a 20 00 02 fe 4d 50 00 00 00 00 00 00 1a 00 48 38 cc f9 0e 00 00 2c 31 ef 46 10 10 00 80 3d 80 08 00 00
+00 00 00 0a 89 a3 40 00 00 00 21 67 b0 00 80 00 00 00 00 00 00 01 91 cf 90 0e 00 15 02 c7 37 8e 04 00 50 20 01 cf 00 10 01
+00 00 00 00 04 93 d6 2c 00 00 13 ab 5f 00 a4 00 00 00 00 00 00 00 0b 0f 58 71 00 01 40 2c 23 bf e3 87 04 80 10 3e a0 0a 00 10
+00 00 00 08 b9 78 e0 60 00 00 22 dc 11 81 00 00 00 00 00 00 10 04 91 c5 98 09 00 42 02 8f d3 a0 00 00 00 0a 94 00 18 10 00
+00 00 00 00 42 da 00 34 70 00 41 1d e8 b0 9a 00 00 00 00 00 30 02 0b 0e e9 f1 84 08 00 20 d7 b4 40 03 00 00 96 80 b0 0f 00 00
+00 02 04 04 a1 a9 40 07 00 00 28 05 c1 01 00 00 00 00 00 00 00 41 91 ff 99 80 00 00 00 05 76 de 38 04 00 08 7d 5e 00 10 00
+00 00 00 00 a5 0e 9e 00 00 80 02 80 20 90 ea 00 00 00 00 01 10 00 0b 0f 78 f0 05 80 00 0a f0 3c 27 c7 20 00 83 df c0 0a 00 00
+00 00 08 4a 07 e0 00 00 50 c0 02 94 c0 0a 00 00 00 00 00 20 00 40 91 c4 8f 9d 00 40 40 8b 5a 20 64 00 00 08 01 db 02 a0 10
+00 00 20 08 a0 04 00 00 52 00 00 00 2f 00 00 00 00 00 00 00 00 00 4b 0e 6c 19 d0 04 00 0d b0 3e c3 00 00 00 80 9c f0 1b 00 80
+00 00 00 01 09 a4 00 28 30 00 10 c7 d5 18 00 00 00 00 00 00 50 40 91 fe c8 18 60 00 0a af 17 20 00 00 00 03 bd de 00 02 00
+00 00 00 00 52 93 80 01 05 00 01 a1 4d f0 a0 00 00 00 00 00 02 00 2b 0c e8 10 99 00 00 20 a0 f8 c0 00 00 80 3f dd f0 00 58 00
+00 00 04 0c 07 66 e0 00 00 00 2b c4 cb 9c 00 00 00 00 00 00 00 20 91 ed 90 0e bd 00 00 05 57 8e 64 54 01 02 fd ea d0 a1 08
+00 00 00 00 ca 0e 26 00 00 02 12 bc 20 71 00 14 00 00 00 00 00 00 0b 0e e9 00 04 08 00 00 50 c1 c3 c5 20 02 2f c0 01 9b 00 00
+00 00 08 08 1f 24 00 40 00 72 01 14 10 01 a0 00 00 00 00 00 00 20 8b d6 a7 8e 00 0c 40 8b 1b a0 78 04 20 0b d4 de 19 da c2
+c0 00 00 00 af db 80 24 00 0a 00 20 2e 90 a4 80 00 00 00 00 00 00 08 2c ed 01 00 00 c0 04 20 e2 c4 00 02 00 c1 41 c0 18 00 2c
+00 00 00 0c 0d 25 60 00 00 00 08 0f e0 00 00 00 00 00 00 06 00 00 01 57 d1 0a 00 00 12 af 5b 80 06 b0 00 1a 3f 5f 01 88 00
+00 80 00 00 ca 96 3e 38 80 0a 20 01 ea 00 c0 00 00 00 00 00 02 00 02 bc 3d b0 00 00 00 20 50 fc 02 20 00 00 83 6d e0 00 1c 00
+00 00 01 24 7d 87 c0 48 24 00 29 ee 90 1c 00 20 00 00 00 00 00 10 31 55 80 01 00 00 00 00 00 34 7c 00 40 0b 1c fe f1 80 01
+40 00 00 12 8b 9b ac 20 00 40 01 47 cc 50 00 80 00 00 00 00 00 00 03 14 00 00 e0 00 00 00 00 02 c7 c0 0d 24 f0 86 e1 00 00 10
+00 00 00 08 3d 21 43 c0 03 10 28 de b0 19 00 20 00 00 00 10 00 00 01 5c 98 1b 00 01 00 af 5b 8c 3f 80 04 0a 3f 4d 81 f0 08
+00 00 00 00 c3 16 80 34 00 30 01 8c 0b 01 f0 02 00 00 00 00 f0 00 02 bc 0d f1 e1 80 01 00 a0 20 22 28 10 04 83 6e e1 18 01 40
+00 02 04 0e 03 ec ee 00 00 10 11 d7 e9 1c 00 20 00 00 00 07 20 10 22 c5 a8 0e 81 00 00 01 b6 3c 00 00 00 1b 1e fe 80 10 18
+00 00 00 00 80 0b 40 00 00 80 01 1c 00 99 00 08 00 00 00 00 01 00 02 2c 20 31 01 80 00 05 bf 6b e3 00 00 00 f0 b6 df 08 01 c0
+00 00 1d 68 53 7d 60 00 00 c0 33 fd b0 01 00 00 00 00 00 00 10 10 28 54 90 00 01 40 40 c5 30 5c 74 00 00 2a 3f 6f 19 94 00
+00 00 20 8a df 0b c2 00 00 00 03 bd d9 00 a0 00 00 00 00 00 00 00 02 ac 3c 00 b0 08 00 0c a0 a0 27 40 00 02 83 6d e0 9d 00 80
+00 00 00 08 e1 aa 60 50 00 80 30 26 d1 1f 00 80 00 00 00 00 50 10 22 5c cf 81 00 00 40 a1 5a c6 02 05 00 4a 86 ff 18 d0 00
+00 00 00 00 0c 52 64 34 10 02 23 80 6e 01 b0 14 00 00 00 02 82 00 03 1c 0b 38 90 00 00 0a b0 e4 00 03 20 40 96 ee 83 8d 00 00
+00 00 04 00 a3 f9 c0 28 00 00 01 5d e8 1b 00 00 00 00 00 00 00 90 11 dc a8 19 20 02 40 83 37 64 24 02 00 08 15 f9 80 15 c0
+00 00 00 00 02 0e 40 01 00 04 40 14 20 b0 80 00 00 00 00 00 00 00 01 1c 20 d0 95 00 00 06 70 6e e7 60 40 00 97 c3 99 09 20 00
+00 00 0d e0 a7 ae c3 40 03 00 00 00 08 19 00 80 00 00 00 00 20 00 00 00 00 00 00 20 00 25 76 40 72 20 30 1a 95 59 80 80 00
+c0 00 00 12 1d 07 a6 30 00 20 00 00 00 70 b0 40 00 00 00 00 01 00 00 00 00 00 00 02 00 00 1b 60 44 10 03 04 95 41 c3 00 00 08
+00 00 00 04 71 64 67 00 00 00 01 c4 91 9e 00 00 00 00 00 00 00 01 00 17 e0 00 00 00 00 e7 7a 60 61 01 04 0a 3d 49 99 e0 80
+00 00 00 00 05 be c0 00 00 00 02 af 6a 01 a0 00 00 00 00 02 08 04 00 0b de 00 00 00 00 00 5b 2f c0 00 00 00 83 8c b5 10 04 00
+00 00 04 08 b3 f9 c3 8a 00 10 01 46 97 1d a0 00 00 00 00 10 10 00 02 1c 07 8e 00 00 00 a1 5e 20 70 82 00 0a bd da 00 f0 01
+00 00 00 00 8b 08 00 28 00 00 01 08 0d 00 80 00 00 00 00 00 a2 00 00 13 6e 00 00 00 00 0a b7 fe c4 10 10 00 81 41 b0 1e 80 54
+00 00 00 0f f5 e4 00 20 00 00 2b d7 d7 00 00 00 00 00 00 05 00 11 00 07 ef 8e 00 00 00 67 be 40 3a 00 00 0a bd fa d0 f0 00
+00 00 00 08 5f 02 c0 00 00 00 00 28 0e 90 00 00 00 00 00 00 00 00 00 04 2a f9 00 00 00 08 39 2c 02 06 20 02 82 81 eb 18 14 40
+00 02 00 00 57 01 43 c0 20 10 28 56 f0 0b 40 00 00 00 00 00 00 00 09 ef e0 09 00 01 40 83 16 be 00 05 00 0a 94 da b8 f0 00
+00 00 00 00 0a 03 40 30 00 00 01 5c 09 30 f1 00 00 00 00 00 02 00 20 5c ff 01 e0 00 00 09 b0 f1 e0 00 20 80 95 43 9f 98 00 00
+00 00 00 40 0d a6 c6 00 00 00 01 5d 90 00 00 00 00 00 00 00 00 80 00 17 e0 00 00 20 00 67 d6 c0 30 04 02 0a bd 7d 80 08 00
+00 00 20 04 01 12 f4 00 00 00 01 7c 3f 00 90 00 00 00 00 00 04 08 60 21 df 00 00 00 00 08 39 6f c0 10 20 00 82 80 b3 09 40 00
+00 00 00 08 17 69 e0 42 20 00 01 5c d5 a8 3c 00 00 00 00 00 50 00 29 5c b0 08 00 02 40 a3 5b c4 32 b0 00 00 17 5d 01 e0 00
+00 00 00 00 5f fb be 24 84 00 02 bc 28 08 8c 00 00 00 00 00 02 00 11 54 18 31 00 00 00 06 30 b4 00 00 00 00 39 ff 81 20 00 00
+00 00 04 08 93 74 63 c8 00 00 20 f6 a3 1e 80 40 00 00 00 00 20 20 3b d4 e9 09 01 40 80 e7 80 20 3c 20 01 02 0d 69 51 84 00
+00 00 00 00 c7 06 be 25 50 00 00 04 f8 b8 04 00 00 00 00 00 74 04 03 ff ee d8 e0 0c 00 0d b9 2a c2 44 00 00 19 c1 a9 00 20 00
+00 00 04 00 33 80 06 00 00 00 00 00 07 98 10 00 00 00 00 00 00 10 08 15 a1 00 00 00 00 f5 56 80 04 05 01 20 1d c8 00 1a 40
+80 00 00 00 03 00 00 40 40 00 00 00 00 08 04 00 00 00 00 00 00 0e 20 80 00 10 80 00 08 0f 50 40 02 c0 20 00 2a c3 f1 0b 40 28
+00 00 00 20 49 fc 06 40 01 00 35 5f d3 80 00 00 00 00 00 00 00 00 29 56 80 18 80 00 00 af 3a 6c 60 01 00 02 15 5a 81 e0 14
+00 00 00 02 1c fa 00 20 00 00 03 94 6e 08 e0 14 00 00 00 00 60 00 01 54 0b 90 02 00 00 00 50 2b c0 00 00 01 3a bd eb 18 18 00
+00 00 00 20 4b a7 40 48 25 10 11 55 b3 9e 40 00 00 00 00 00 50 00 11 76 90 00 00 20 00 05 32 a0 00 50 00 02 9d ec 00 00 00
+00 00 00 02 2c bb 84 20 05 40 01 3c 39 0b 00 02 00 00 00 00 40 00 02 3f dd 00 00 00 00 05 f0 f1 40 00 00 00 06 56 8d 0d 00 00
+00 00 00 20 2b 27 67 00 00 10 01 46 db 3e 00 00 00 00 00 05 00 00 2a df d1 9c 00 00 00 47 5e cc 00 00 00 02 85 49 38 80 80
+00 00 00 02 25 33 d4 00 00 00 01 08 0c f1 00 00 00 00 00 00 00 00 00 2d 5f 00 01 40 00 05 f7 70 23 c0 00 00 2a e4 c0 8d 08 00
+00 02 00 60 4d 34 67 a3 10 00 20 5d c1 00 a0 00 00 00 00 02 20 10 20 dc d0 09 b0 00 40 05 36 be 3a 01 01 02 0d 5a 01 c0 00
+00 00 00 02 2c bf 80 02 00 00 00 17 6c 78 04 00 00 00 00 00 60 00 01 9c 3d 01 f0 00 00 0d b0 77 c7 d0 00 02 19 c2 af 1b 00 00
+00 00 00 20 4d 7f e6 00 08 04 39 7e 80 09 01 40 00 00 00 14 00 00 2a df d0 00 a0 20 00 47 56 dc a6 00 00 00 00 00 00 09 04
+00 00 20 02 1c fb 84 78 20 00 01 02 fd 90 f0 08 00 00 00 00 00 00 00 2d dd 70 00 00 20 00 5f 73 62 12 00 00 00 00 07 0d 00 00
+00 00 00 20 4f 67 e0 20 00 00 01 54 d0 19 00 80 00 00 00 00 40 10 20 df d1 1c 00 00 00 a5 5e 40 e8 00 08 02 bc d8 00 00 02
+00 00 00 02 2c b7 a4 34 00 00 01 7c 0b 90 b0 14 00 00 00 00 02 00 01 9c 0d f1 e0 00 00 05 50 7c 07 80 00 00 01 43 a0 09 00 00
+00 00 00 20 4f 7c 02 80 00 00 00 00 07 80 00 00 00 00 00 10 00 10 31 57 c5 8c 40 00 48 ab 32 64 78 85 00 02 95 5a 11 80 00
+00 00 00 02 1c fa f4 40 00 00 00 00 00 00 00 00 00 00 00 02 a0 00 03 14 00 09 0a 00 00 00 70 ed e4 12 30 00 15 40 a0 80 00 00
+00 00 00 20 05 7d e6 00 02 00 00 00 00 0e 60 00 00 00 00 00 00 00 00 00 01 1a 80 00 00 a5 5a 20 00 00 00 02 bc 68 38 00 00
+00 00 00 02 02 b7 80 34 00 30 00 00 00 10 04 00 00 00 00 00 02 00 00 00 00 91 80 00 10 0a a0 7f c0 00 00 80 01 40 80 80 18 00
+00 00 01 2a 53 b8 60 68 00 00 22 5e e0 01 00 80 00 00 00 08 20 00 2a df 8f 00 5c 00 00 a1 1a 00 7c 44 88 08 14 c8 b9 e3 d8
+00 00 00 12 9a ba 00 20 30 00 03 2c 7e 00 ea 04 00 00 00 01 08 00 00 1d dc f8 0a c0 00 0a bb 24 c7 80 20 00 97 c0 c7 88 00 00
+00 00 00 6a f5 68 e0 00 05 10 01 dd d8 1f 00 00 00 00 00 02 00 10 28 cd d5 8a 00 00 40 83 57 40 79 60 00 09 1d f9 00 f0 81
+00 00 00 0a 0a 03 82 34 00 50 00 08 3c b0 f0 00 00 00 00 00 00 00 42 4c 08 09 00 00 00 06 70 a0 00 08 00 04 b0 f6 b0 1e 00 10
+00 00 01 a8 df 04 06 00 00 00 20 cf 90 00 a0 00 00 00 00 00 10 00 29 5c d0 00 40 00 00 a1 1a 4e 70 00 00 0a 3d df 70 10 80
+00 00 00 02 4e 5b c0 00 00 00 00 07 ed f0 04 00 00 00 00 00 00 00 02 a8 0e 00 a2 00 00 0a b5 3c 00 00 08 00 83 b7 f7 8d 40 00
+00 02 10 2c 13 35 e7 a0 00 00 11 c6 c5 80 40 a0 00 00 00 00 00 10 30 16 c0 1e 00 00 40 83 3b 60 72 80 00 1b 1d ca 99 82 00
+00 00 00 0a c7 9b 42 00 40 00 00 17 7d 08 0a 00 00 00 00 00 20 00 43 28 1d 01 94 00 00 06 70 a2 44 02 00 40 b0 af a5 08 00 20
+00 00 00 2a 53 fe 00 00 00 10 01 4c f9 0f 00 00 00 00 00 09 00 00 2a de 83 8e 50 80 40 c5 30 00 38 00 00 09 1f d8 18 94 00
+00 00 20 82 65 bb ec 34 00 00 13 6c 1e 31 d0 00 00 00 00 01 00 00 00 1e 5c 71 8a 10 00 0c a0 38 c2 a0 00 20 b0 cc f0 08 00 40
+00 00 01 2c 3b b7 c6 07 00 00 28 7f 87 18 04 00 00 00 00 00 20 10 28 cd d8 00 60 00 40 42 72 60 00 00 00 0a 3f 6a 80 14 00
+00 00 00 12 47 3f 76 00 00 00 02 89 cd 78 00 00 00 00 00 00 00 00 02 4c 1a f0 94 04 00 44 70 b9 c3 40 00 04 83 bd c1 08 20 00
+00 00 00 2a f7 bf e7 40 00 00 35 6c e5 9f 0c 00 00 00 00 00 30 00 03 34 e8 0d 80 80 00 0c 5f c4 61 00 40 09 1f 78 90 10 00
+00 00 00 02 05 0a c6 7c 00 00 03 97 6c 18 e0 00 00 00 00 00 12 00 42 35 5d b0 e0 14 01 04 e1 a8 20 20 00 00 b0 ec f1 0e 00 00
+00 00 10 2e 78 3f e0 60 02 00 00 00 08 08 08 00 00 00 00 00 20 00 00 00 00 19 00 00 00 00 00 14 00 02 00 0a 94 5a 71 e2 08
+c0 00 00 0a 42 97 c4 20 00 20 00 00 00 11 c0 04 00 00 00 00 05 00 00 00 00 00 f2 00 00 00 00 00 03 00 50 80 aa 83 91 10 41 0c
+00 00 00 ac 73 e2 60 40 00 04 a3 f4 97 bf 00 10 00 00 00 06 40 00 b8 d7 d0 01 00 00 00 25 d2 2e 38 44 00 03 94 ff 00 00 08
+00 00 00 02 c2 37 84 3c 00 02 08 35 4b 38 e0 00 00 00 00 00 00 00 09 0c 6b 90 80 00 00 06 71 a0 c6 40 00 00 11 4d a0 00 00 00
+00 00 00 a8 fd 79 c0 40 04 01 91 c5 b8 08 00 10 00 00 00 00 00 00 83 75 f8 01 09 14 40 83 33 40 20 20 01 03 15 ea 00 01 00
+00 00 00 02 0e 92 c0 20 00 48 6b 0e f8 71 f0 01 00 00 00 00 00 00 4a 3c cb 10 81 8d 00 06 70 a8 07 c0 00 00 31 41 05 00 18 00
+00 00 01 24 73 e4 66 00 00 01 91 d4 d9 8f 04 00 00 00 00 00 00 40 b8 d7 d1 98 00 00 00 25 9a 24 78 22 01 23 89 4d 18 80 80
+00 00 00 82 c3 9f 9c 00 00 00 4b 0e ed b0 82 00 00 00 00 01 00 00 49 0e ef 00 00 00 00 06 71 27 40 00 50 00 20 41 d7 1e 80 00
+00 02 00 24 73 fc 00 00 00 01 ab d4 e7 89 00 00 00 00 00 00 10 80 83 74 99 18 88 00 40 83 73 c0 00 00 80 01 17 f9 00 e0 80
+00 00 00 12 c3 9a 40 00 00 00 08 28 38 09 80 00 00 00 00 00 02 00 0a 3c fb 78 01 c0 00 06 70 68 03 c0 08 00 23 f7 cd 00 04 00
+00 00 00 2c 7b bc 06 40 00 00 ab d7 c9 1a 00 20 00 00 00 00 20 00 b8 f7 c8 1e 80 00 40 47 37 a0 64 00 00 22 ae dc d0 e0 00
+00 00 20 02 c2 17 40 20 00 02 08 28 1a 19 0a 00 00 00 00 00 01 02 09 0c 6e 10 f0 00 00 04 70 02 43 40 00 00 02 cc ff 08 00 00
+00 00 00 00 55 64 c7 00 00 00 81 54 ed 80 00 00 00 00 00 00 20 00 83 77 d9 88 00 00 80 af 57 a0 70 60 00 41 0b dc 01 c0 d8
+00 00 00 10 af 0b bc 38 00 00 29 7c 1f f8 00 04 00 00 00 00 31 00 0a 3c db 70 f0 00 00 00 a0 a4 c4 00 50 00 11 dd a9 1f 00 80
+00 00 00 00 53 7a 03 80 00 40 a3 fe cd 80 00 00 00 00 00 07 00 04 b8 f6 c1 08 00 00 00 e5 df 16 61 00 00 00 95 4c 78 80 08
+00 00 00 80 05 00 1c 40 00 00 28 3a ea f8 00 10 00 00 00 00 02 00 0a 0e 6f 01 00 00 10 04 5b 28 04 0e 00 00 19 ee a0 80 01 40
+00 00 00 00 53 04 07 44 03 00 ab c6 e8 00 00 08 00 00 00 20 00 00 91 f6 17 8e 40 0c 00 00 00 00 74 00 00 00 00 00 d0 f0 10
+00 00 00 80 0a 03 80 2c 00 20 18 28 39 10 00 00 80 00 00 01 80 04 0a 2e ef f8 00 00 80 00 00 00 03 01 00 00 00 00 01 8a 00 00
+00 00
+11 03
+01 01
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 38 00 80 00 00 00 00 00 02 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 30 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 08 00 00 00 00 00 00 20 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 08 00 00 00 00 00 00 20 00 00 00 00 0c 00 00 00 00 00 00 30 04 00 00 00 00 00 40 00 00 00 00 00 03 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 20 00 02 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 08 00 00 00 00 00 00 20 00 00 00 00 00 00 80
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 88 00 00 00 00 00 00 20 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 04 00 00 02 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 02 00 03 00 00 00 00 08 00 00 00 00 00 00 00 00 b9 c0 90 00 00 0c 00 f0 0e 80 00 00 00 01 e3 5b 00 80 00
+80 00 00 00 00 00 00 3c 00 30 00 00 00 00 00 00 00 00 00 00 00 01 0b dd c9 00 80 00 c0 00 f1 ed 60 00 00 00 12 ff c0 19 20 0c
+00 00 01 66 90 00 02 00 00 00 00 00 01 00 00 00 00 00 00 01 00 00 21 42 80 10 00 00 00 f0 0a c4 00 00 00 09 d1 68 00 00 00
+00 00 01 06 69 16 80 64 00 00 00 00 00 01 a0 00 00 00 00 00 00 01 00 14 6c 79 80 00 00 00 f7 27 60 00 00 00 a9 05 97 18 40 00
+00 00 01 26 90 00 00 00 00 00 3f ec c0 0a 00 00 00 00 00 00 00 00 00 02 b1 00 00 00 00 33 b6 42 00 00 00 48 94 19 10 90 00
+00 00 01 83 69 16 80 00 00 00 03 bd dd 01 b0 00 00 00 00 00 00 00 00 14 cb 80 00 00 00 0c c9 6d c0 00 00 10 8b 86 88 00 00 00
+00 02 01 26 90 00 c0 00 00 00 00 00 00 0a 00 00 00 00 00 00 00 00 00 00 09 10 00 00 00 69 54 14 00 00 00 03 04 08 00 c0 08
+00 00 00 83 69 17 a0 00 00 00 00 00 00 58 10 00 00 00 00 00 00 00 00 00 00 81 80 00 00 16 91 62 06 80 00 90 10 86 90 00 00 40
+00 00 08 26 90 40 03 80 00 00 00 00 00 10 00 00 00 00 00 00 00 00 9d 0f 95 00 00 00 10 69 b4 14 20 00 00 01 dc 0b b0 00 00
+00 00 20 93 69 12 80 64 00 00 00 00 00 01 80 00 00 00 00 00 00 00 09 60 ed d8 00 00 00 16 99 3b 46 c0 00 10 11 1c 88 00 00 00
+00 00 08 26 90 40 40 00 02 00 00 00 00 00 00 00 00 00 00 01 00 00 ae 36 d0 00 00 0c 00 69 b4 00 28 00 00 03 c2 fe 71 00 00
+c0 00 00 13 69 12 a6 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 09 63 6d 01 a0 00 c0 16 91 68 06 c0 00 00 38 75 b0 08 00 0c
+00 00 00 a6 90 00 00 00 00 00 3f fc e0 00 00 00 00 00 00 00 00 00 9d 0e 90 00 00 00 00 69 30 14 00 00 00 09 4e 4e 00 a0 00
+00 00 00 93 69 13 80 00 00 60 03 bc db 00 00 00 00 00 00 00 00 00 09 60 eb 58 00 01 80 16 91 69 40 00 00 01 a8 c6 10 0a 00 18
+00 00 02 a6 90 00 00 00 00 00 00 00 00 00 04 00 00 00 00 04 00 00 00 00 00 0a 00 00 00 69 f0 00 00 00 00 49 fc d8 50 90 00
+00 00 01 13 69 13 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 16 98 28 06 00 00 00 8f de 91 8b 00 00
+00 00 09 26 90 40 02 00 02 00 00 00 05 0a 00 00 00 00 00 00 00 00 80 04 90 00 00 0c 82 11 02 82 00 00 20 4b c0 00 01 00 00
+c0 00 01 87 69 17 86 04 00 20 00 00 00 00 10 00 00 00 00 00 00 00 18 92 18 00 00 00 84 2d d3 ec 60 00 02 00 bc 0c 00 1e 00 0c
+00 00 09 a6 90 40 22 00 00 00 00 00 05 0a 00 00 00 00 00 00 00 00 82 82 90 00 00 00 00 c3 07 02 00 00 00 03 25 79 90 01 c0
+00 00 00 07 69 16 9e 64 00 00 00 00 00 81 b0 00 00 00 00 00 04 00 48 00 59 00 80 00 04 0a 50 34 e0 00 00 00 0d 94 e8 00 00 00
+00 00 00 a6 90 00 00 10 00 00 01 40 05 00 00 00 00 00 00 00 00 00 80 04 97 00 00 00 00 87 08 06 20 00 00 02 5a 6d 00 00 08
+00 00 00 83 69 16 80 00 30 04 00 15 6c d8 00 00 00 00 00 00 00 00 18 92 08 80 00 00 04 08 71 2b 46 50 00 00 0f 27 95 80 00 00
+00 02 00 26 90 00 00 00 00 00 3f c3 d5 0c 40 80 00 00 00 00 20 00 82 82 90 00 05 00 00 f0 08 02 00 84 00 00 cc 39 90 e0 00
+00 00 00 87 69 17 80 00 00 01 03 bc ec 58 0a 14 00 00 00 00 00 00 18 00 58 00 00 08 00 0f 01 80 e0 10 20 10 33 0d cd 80 00 00
+00 00 00 26 90 40 48 08 40 00 3f ff f0 88 00 00 00 00 00 00 10 00 82 04 90 90 00 00 00 a5 0b 54 6c 00 00 03 e8 2b 50 00 00
+00 00 20 03 69 12 a0 61 02 00 03 bf 7c 18 90 00 00 00 00 00 02 02 08 13 c0 71 e0 00 00 00 f7 f6 62 80 00 00 02 8c a0 00 00 00
+00 00 00 26 90 40 40 05 03 24 3f c0 a0 10 00 00 00 00 00 00 00 00 82 82 a0 80 00 0c 00 f0 07 c0 40 00 30 03 c0 38 51 00 00
+00 00 00 03 69 12 a6 00 00 31 13 bd 78 00 a0 00 00 00 00 00 00 00 08 00 58 79 e0 00 80 00 f3 e0 c2 80 02 00 03 cf b0 0a 00 00
+00 00 00 26 90 00 03 00 00 00 3f dc e0 19 00 00 00 00 00 00 00 00 82 06 97 0e 00 00 00 22 5a 00 6c 20 00 03 e8 1d 58 a0 00
+00 00 00 03 69 13 80 00 30 60 03 bf d8 00 b0 00 00 00 00 00 00 00 08 13 60 81 d0 01 80 0d db f4 62 c2 06 00 17 c7 95 00 00 18
+00 00 02 26 90 00 00 01 00 00 00 00 00 00 00 a0 00 00 00 00 a0 20 82 82 df 0f 00 00 00 dd 3e f4 00 00 00 0b c0 20 d0 00 00
+00 00 00 03 69 13 80 00 70 00 00 00 00 00 00 14 00 00 00 00 80 00 08 00 59 b8 10 00 00 02 29 6c 60 00 00 04 bc 0e 00 00 00 00
+00 00 00 26 90 40 02 08 02 40 80 00 e0 10 00 0c 00 00 00 00 00 00 20 ec b0 18 00 0e 00 67 0a 40 00 00 30 03 48 10 00 00 00
+80 00 00 03 69 17 86 05 00 20 08 15 c8 01 c0 00 c0 00 00 00 00 08 01 9d c8 00 80 04 c4 08 35 bc 02 00 02 10 34 87 e5 00 00 0c
+00 00 00 26 90 40 00 00 40 00 00 00 00 1f 00 00 00 00 00 00 00 04 14 66 f0 88 00 00 00 9b 13 94 00 44 00 01 a4 e9 b0 00 00
+00 00 00 83 69 16 80 60 00 00 00 00 00 01 e0 00 00 00 00 00 00 00 02 9f 4e 31 90 00 00 02 53 24 02 80 20 40 25 94 08 18 00 00
+00 00 00 26 90 00 00 08 00 40 80 01 e0 09 00 00 00 00 00 00 00 24 22 e6 c9 00 00 00 00 67 9a c0 00 00 00 01 7c 2b 30 80 00
+00 00 00 83 69 16 80 00 40 00 08 15 c8 00 00 00 00 00 00 00 00 00 01 28 ea 80 80 00 10 02 5b 79 c0 00 00 20 2b c4 08 00 00 00
+00 02 00 26 90 00 00 28 00 04 80 00 8f 19 00 00 00 00 00 10 00 04 38 95 d0 90 00 02 00 63 0f 04 30 00 00 00 e4 39 00 e0 00
+00 00 00 03 69 17 80 01 00 00 08 15 cd 01 e0 00 00 00 00 00 80 00 02 84 c9 32 e0 00 04 0a 31 ba 06 00 40 10 32 54 e0 00 00 00
+00 00 00 26 90 40 40 08 00 80 80 01 e0 80 00 00 00 00 00 00 00 04 80 02 cf 0e 00 00 00 67 92 96 28 00 02 02 80 2b 00 f0 00
+00 00 20 03 69 12 a0 61 02 00 08 15 d8 10 00 00 00 00 00 00 00 00 08 14 eb 80 10 04 00 02 5d 7c 43 80 00 00 01 44 c0 01 20 00
+00 00 00 26 90 40 40 00 02 00 80 00 c0 00 00 08 00 00 00 00 00 00 80 00 df 1e 00 08 02 04 18 14 00 00 20 09 3a ce 50 80 00
+00 00 00 03 69 12 a6 00 00 20 48 15 cc 00 00 00 c0 00 00 00 00 00 08 15 5b 81 e0 00 80 20 4d e0 02 03 02 01 a3 76 08 01 00 00
+00 00 00 26 90 00 03 90 00 04 80 01 e9 09 00 00 00 00 00 00 00 00 80 02 e7 00 00 00 02 00 06 ce 70 00 00 08 28 1a 00 80 00
+00 00 00 03 69 13 80 00 70 60 08 15 da 00 00 c1 80 00 00 00 00 00 08 14 eb 80 00 01 80 2a 01 e7 c3 c0 06 00 ab dd c5 81 00 18
+00 00 00 26 91 03 c2 0e 00 04 80 00 c1 08 00 00 00 00 00 00 00 00 80 01 df 0a a1 01 02 05 0e 04 3a 80 00 0a bc 2d 10 00 00
+00 00 00 03 69 02 00 7c 00 00 08 15 cd 00 00 00 00 00 00 00 00 00 08 15 5b 70 04 08 00 20 05 f4 02 d0 00 00 81 46 b8 00 00 00
+00 00 01 20 00 ac 20 00 03 00 80 01 d0 90 00 0c 00 00 00 00 00 00 18 c0 c3 10 00 0d 00 0a 0a 02 00 30 30 0a a8 10 00 00 00
+c0 00 00 02 05 3e 4e 30 70 28 0a 81 5c 10 80 00 80 00 00 00 00 03 02 8c da b9 e0 00 d0 00 07 bc 67 07 03 00 aa 8f a0 00 00 08
+00 00 00 20 11 c0 24 00 00 20 81 42 a0 80 00 40 00 00 00 00 00 00 11 55 c1 8d 00 01 00 00 03 c0 00 00 00 09 37 6c 10 00 00
+00 00 00 02 01 df 1e 30 00 00 08 00 dc 10 00 08 00 00 00 00 00 00 02 3d ca b8 10 00 00 00 51 b4 03 80 40 00 a3 b6 18 18 00 00
+00 00 00 20 00 ae e0 00 00 20 90 2c d9 9d 80 00 00 00 00 10 00 00 10 de ff 00 00 00 00 00 0a 00 00 04 04 03 fc 3b 00 88 00
+00 00 00 02 05 7e 6c 38 00 00 0a 03 40 10 90 00 00 00 00 00 00 00 01 9d cc 01 c0 00 20 00 a7 bc 00 00 20 50 3b d6 b0 00 40 00
+00 02 01 20 00 a0 20 00 04 04 82 82 80 80 00 10 00 00 00 18 00 40 2b 6f 90 1e 14 10 02 01 0a bc 00 00 40 02 bc 10 00 00 15
+00 00 00 02 05 5b 4c 38 01 00 08 00 c9 78 00 00 00 00 00 03 00 00 10 32 5e 01 90 00 04 20 25 00 03 80 00 00 2b d4 e0 00 a0 80
+00 00 00 20 00 2e c0 00 00 04 80 01 d0 10 00 00 00 00 00 00 00 01 12 bf 90 80 00 00 04 05 0a 02 00 00 00 08 3d 6f 11 00 00
+00 00 20 02 05 7e 40 78 40 00 0a 81 4e 01 e0 00 00 00 00 00 00 00 01 1e e8 79 e0 00 00 80 07 bc 40 00 00 00 a3 94 e9 9e 00 00
+00 00 01 20 00 a0 00 10 03 00 82 82 e0 80 00 4c 00 00 00 00 00 00 80 02 8d 00 18 0c 02 05 0e 9c 00 02 20 0b 30 20 00 80 00
+00 00 00 02 05 5f 40 00 80 30 08 00 c9 71 e0 08 80 00 00 00 00 00 08 14 5c 19 80 00 80 20 01 62 03 80 43 10 b3 1c 01 81 00 00
+00 00 00 20 00 2c 03 c0 00 00 80 01 d0 09 00 80 00 00 00 20 00 00 80 03 90 00 00 00 02 00 06 40 40 00 00 00 00 1b 00 00 02
+00 00 00 02 05 76 40 04 00 60 0a 81 5e 00 00 11 80 00 00 00 00 00 08 14 fe 00 00 01 80 2a 05 69 c7 00 06 00 01 4f b0 00 00 18
+00 00 00 20 00 e0 00 00 00 00 82 82 c0 00 00 00 00 00 00 00 88 00 80 02 b1 08 14 00 00 ff 02 80 00 05 00 08 3c 1e 00 04 00
+00 00 00 02 05 5b 40 00 00 00 08 00 c9 01 e0 08 00 00 00 00 00 00 08 14 5d 58 00 00 29 4a f1 b0 60 00 20 00 aa 94 e0 00 a0 00
+00 00 00 05 17 bf c3 60 00 00 19 c0 b0 80 01 00 00 00 00 00 00 00 80 01 fd 80 00 0c 04 a7 16 80 00 00 30 02 00 0d 00 e0 00
+80 00 00 08 a7 37 e0 7d 00 05 02 0d cb 70 e0 08 00 00 00 00 00 00 2a 80 cf 70 80 00 c8 00 3b bc 03 80 03 10 00 0c a0 0b 00 08
+00 00 00 46 70 f7 42 c0 00 00 09 b5 df 0a 00 00 00 00 00 02 00 00 82 82 b0 8e a0 00 00 57 b6 42 40 00 00 01 40 2d 31 00 01
+00 00 00 40 83 72 20 00 80 00 02 9e cc 71 c0 00 00 00 00 00 00 00 08 01 ee 10 04 00 20 00 73 b0 43 80 00 00 28 0e 00 1e 00 00
+00 00 00 05 f5 ff e0 00 00 00 19 c2 b0 0f 00 00 00 00 08 00 00 00 80 01 f0 00 18 00 00 17 b2 8c 24 00 08 02 b5 58 00 00 00
+00 00 00 00 81 53 6e 00 30 05 02 0c 69 00 11 c0 00 00 00 00 00 00 0a 80 db 00 00 00 01 02 77 22 e2 c0 00 00 03 3e c3 00 00 00
+00 00 08 06 70 f4 e0 05 44 00 09 b5 c7 0e 0c 30 00 00 00 10 00 80 82 82 ab 0e 18 31 04 5e 3b c6 00 00 40 20 94 3e 10 e0 01
+00 00 00 40 83 56 2e 03 01 00 02 9f cc 78 a0 00 00 00 00 00 00 00 08 01 ee 80 01 d2 00 00 c9 ea e0 00 40 10 29 5d a7 80 00 00
+00 00 00 05 fb 7d 47 80 00 00 19 c3 bb 80 00 00 00 00 00 00 00 00 80 00 f0 00 00 00 04 2e 56 40 3c 20 00 02 bc 0d 10 a0 01
+00 00 00 00 81 5b 40 28 30 01 42 0c 4f 90 00 00 00 00 00 00 00 04 0a 80 db 79 e0 00 00 02 5b bc 00 40 00 00 17 cc 08 00 a0 00
+00 00 00 86 70 75 e3 92 00 00 20 fe bd 10 00 00 00 00 00 00 00 00 82 82 90 b0 00 08 02 01 3c 00 28 00 30 08 00 1e 08 00 00
+c0 00 00 40 83 5e 94 00 85 00 01 9f c9 01 80 00 00 00 00 00 00 00 18 01 ef 31 e0 00 c0 20 19 f0 60 40 02 00 81 57 97 1e 00 08
+00 00 00 05 fb ff 60 08 50 00 19 c0 b9 0d 00 00 00 00 00 00 00 01 80 01 ff 00 00 00 02 00 06 42 40 00 00 08 05 50 98 c0 00
+00 00 00 00 81 9b 4e 31 32 81 42 0c 7f 00 10 00 00 00 00 00 70 00 0a 80 c9 80 00 01 80 20 53 f5 42 81 06 80 80 7f 81 01 00 18
+00 00 00 88 9b 34 00 15 00 00 2b cf bb 19 04 00 00 00 00 02 00 01 82 82 ff 00 00 00 02 00 0b 14 60 00 00 08 00 0e 00 00 00
+00 00 00 00 c7 fe 14 02 81 80 02 07 4d 80 b0 00 00 00 00 00 00 00 08 01 ef 80 00 00 00 20 51 a6 03 c0 00 00 81 57 f0 1e 00 00
+00 00 00 20 29 bd e0 00 02 00 19 c0 d7 19 00 00 00 00 00 00 00 00 24 c0 a0 0f 00 0c 00 00 00 1e 00 00 00 11 3c 2e 01 80 00
+80 00 00 12 01 38 6c 30 04 25 02 0d fc 00 80 00 00 00 00 00 00 01 02 8d 48 10 80 00 80 00 00 00 e0 02 00 10 2b 9d f0 0f 00 08
+00 00 00 20 00 fc 24 08 00 00 06 7e e0 1d 00 00 00 00 00 10 10 00 14 6e c1 80 00 00 00 f0 08 2e 00 00 00 01 9c 08 10 00 00
+00 00 00 02 05 7a 16 30 01 00 02 9c 5c 38 f0 00 00 00 00 02 80 00 02 9d d8 79 80 02 08 0f 03 02 c0 10 50 10 20 d7 cf 8c 00 00
+00 00 08 20 29 3e c0 00 00 04 19 c0 d0 0a 00 00 00 00 00 00 40 00 05 fd b0 08 00 81 80 05 0c 2e 01 00 00 01 0f cc 01 80 00
+00 00 00 02 01 78 40 01 00 01 02 0c 7a 00 f4 00 00 00 00 00 02 00 00 9d fa 00 01 c0 00 80 53 fa e2 00 00 00 19 dc 80 0d 00 00
+00 02 00 20 00 e8 00 00 00 00 06 7c e5 0c 00 00 00 00 00 00 00 00 26 c1 ef 1b 00 00 00 00 0b fe 40 00 0a 48 64 0c b1 00 12
+00 00 00 02 05 7a 00 00 00 00 02 9d da b8 e0 00 00 00 00 00 00 01 02 0c ce 00 f0 00 10 40 a7 76 e7 00 20 13 91 94 98 1e 00 a0
+00 00 00 20 29 3c 00 00 00 00 19 c2 d0 1f 00 00 00 00 00 00 00 00 11 5d f0 0b 00 00 00 00 00 00 40 00 00 00 00 0f 00 d0 00
+00 00 20 02 01 70 40 3b 00 09 02 0d 4c 00 f0 c0 00 00 00 00 00 00 02 3e d8 79 d2 02 00 00 00 00 07 ad 00 00 29 40 80 01 80 00
+00 00 00 20 00 e8 00 00 03 00 06 7e e0 00 80 00 00 00 00 10 20 00 82 82 f1 88 00 0c 10 00 ba 96 28 00 08 08 14 09 00 90 00
+80 00 00 02 05 7a 5e 38 00 20 02 9d f8 50 00 00 00 00 00 02 05 00 08 00 5a f8 f0 08 a0 40 4b e7 e0 45 00 85 96 94 81 80 00 0c
+00 00 00 20 29 be c3 00 00 40 19 c3 d0 1a 20 00 00 00 00 00 00 00 80 00 9d 1b 00 00 00 2b ff 0c c0 40 04 49 1d 68 00 e0 00
+00 00 00 02 01 18 60 05 00 61 02 0c 4c 01 a4 80 00 00 00 00 00 00 0a 80 7c 00 b0 01 80 02 7b 60 03 80 00 00 a0 15 c0 00 00 18
+00 00 00 20 00 fc 00 00 00 00 06 7c e9 80 00 00 00 00 00 00 80 00 82 82 f0 89 00 00 00 0b fa c0 00 00 00 09 54 28 10 80 00
+00 00 00 02 05 7a 4c 00 00 00 02 9d ec 90 00 00 00 00 00 00 00 00 08 00 5b 70 01 00 01 0a b5 28 00 00 00 a5 aa 9c 08 19 20 00
+00 00 00 20 00 74 00 00 02 40 82 36 f0 00 00 08 00 00 00 00 08 00 29 cd b0 98 00 0d 00 c7 02 42 2d 00 20 02 94 2c 11 b1 80
+40 00 00 82 05 52 4e 01 00 20 08 11 c0 38 01 00 c0 00 00 00 00 84 00 07 df 58 90 10 c4 08 93 78 43 20 02 00 02 9c e9 0e 00 0c
+00 00 00 20 00 f0 23 40 00 04 82 83 8f 80 00 00 00 00 00 04 00 40 16 c5 90 8e 00 00 00 db 0e ee 64 00 00 0c 3c 20 09 00 14
+40 00 00 82 05 3a ce 74 00 80 08 01 ca 10 00 00 00 00 00 00 00 00 00 04 fb 71 91 00 04 00 55 21 e2 00 00 02 83 cc 07 9e 00 8c
+00 00 00 20 00 f4 c2 d6 20 00 82 34 f0 2e 00 20 00 00 00 00 00 80 20 5d c3 0d 00 00 00 81 0f 8c 6d 70 00 02 2c eb 10 00 00
+00 00 00 82 05 3a 5e 07 35 00 28 11 60 00 04 08 00 00 00 00 00 00 03 9e 0d b8 d0 00 04 08 b7 ba 0b 00 00 01 21 6c 89 8e 00 00
+00 02 00 60 00 fe c0 00 08 00 82 83 b0 09 1c 00 00 00 00 01 00 00 81 43 cf 1e b4 00 00 af 06 04 78 00 0a 40 00 3d 78 00 00
+00 00 00 02 05 3a c0 00 00 00 08 01 cb 02 00 00 00 00 00 00 50 00 0a bc 4a 00 b0 00 00 00 53 62 03 c0 20 21 28 1f 85 1e 00 00
+00 00 00 20 00 f4 00 00 00 00 82 36 f0 90 00 00 00 00 00 00 00 80 91 ec e1 2b 00 00 00 6e 9b c0 00 00 04 00 14 3f 00 00 00
+00 00 20 82 05 32 40 00 02 00 08 10 60 10 80 00 00 00 00 00 00 00 0a 2e e0 01 e0 10 00 08 5b 38 00 00 10 07 00 0d f0 00 00 00
+00 00 00 20 00 a4 c4 00 03 20 82 83 c1 10 a0 08 00 00 00 10 00 00 81 40 c7 00 00 0c 02 0a 02 9e 40 20 20 42 bc 2d 71 04 10
+00 00 00 02 05 3b a0 68 00 b0 08 01 ca 00 80 00 80 00 00 02 80 00 0a bc 6c 80 00 00 c0 20 03 f9 e3 80 02 a4 17 de 08 1e 80 90
+00 00 00 20 00 74 03 90 48 20 82 34 f9 00 9c 00 00 00 00 28 00 00 97 c2 a9 90 00 02 02 00 07 3e 39 40 00 23 00 20 00 00 00
+40 00 00 06 05 72 40 00 b2 60 08 11 40 00 01 41 80 00 00 01 00 00 08 14 ed 50 a0 01 80 2a 03 e5 c0 28 06 10 30 0e 01 00 00 14
+00 00 00 60 00 e0 c2 c0 00 00 82 83 f1 08 00 00 00 00 00 00 00 00 82 82 a0 08 10 00 02 0a 03 66 34 00 00 00 11 6e 00 80 00
+00 00 00 02 05 3a a0 04 00 00 18 01 cb 00 00 00 00 00 00 00 00 00 0a bc 6f 58 14 00 10 20 03 fd c0 00 00 00 00 0f f0 01 00 04
+00 00 08 08 b0 a0 23 c0 02 00 01 42 dd 90 00 88 00 00 00 00 00 40 11 c3 d9 00 1c 01 02 05 0a 40 30 e0 32 09 1e ed 00 90 02
+80 00 00 40 47 58 5c 01 00 20 02 bd 6e 90 c4 00 80 00 00 00 00 01 01 08 4f 00 01 80 00 2a f7 f8 67 40 02 01 b0 f5 80 00 00 ac
+00 00 00 00 50 3c 60 00 60 00 97 c2 cb 00 01 00 00 00 00 18 20 00 2a 80 b0 00 0c 00 06 55 0b 40 20 01 04 0b 0e 4d 09 00 10
+00 00 00 00 af 7e b6 60 01 00 08 15 ea f1 80 00 00 00 00 02 d4 00 02 94 e9 10 80 00 00 2a 51 7c 43 40 00 00 91 f7 f3 9e 00 80
+00 00 00 a0 50 67 c3 00 50 00 01 42 d1 8d 00 00 00 00 00 00 00 80 01 41 c9 00 00 00 80 67 0f 04 3a 80 00 48 39 d8 90 e1 00
+00 00 00 02 af 5b 00 00 02 04 02 bc e8 b8 10 00 00 00 00 00 00 00 02 bd cb 38 80 00 04 08 33 b2 03 80 00 00 a3 f7 d1 80 00 00
+00 02 00 60 a0 7d c0 11 00 00 82 82 db 9f 00 00 00 00 00 00 10 00 02 80 a0 00 00 80 00 0b 13 c6 02 62 00 2b 0c ed 08 f0 00
+00 00 00 02 af 5a 60 02 00 04 0a bd ca 10 c0 00 00 00 00 00 12 04 02 bd 5c 00 00 04 00 0a bb b7 43 90 00 01 91 d7 f7 00 00 00
+00 00 00 a0 50 a4 02 00 20 02 81 42 a0 00 80 00 00 00 00 00 00 00 00 00 07 80 00 00 02 0a 0b 80 00 04 08 0b 0e 7e 70 98 00
+00 00 20 12 af 57 16 64 54 00 0a bd fc 7a 00 14 00 00 00 00 00 00 00 00 00 f0 a0 00 00 2a f5 24 07 83 20 01 91 dd d8 18 00 00
+00 00 00 20 a0 f4 c2 38 00 04 82 82 b0 8a a0 28 00 00 00 04 40 00 20 43 f0 0a 00 80 02 5f 0f 40 01 10 00 0b 0e 6d f0 04 08
+00 00 00 8a af 72 e6 7c 01 00 0a bc ea 10 04 02 80 00 00 00 72 09 01 1d 7c 59 a0 00 10 20 53 20 67 88 00 80 91 d5 ff 80 21 00
+00 00 01 20 50 2c 03 c0 25 00 81 42 a7 09 00 40 00 00 00 00 02 00 01 41 c5 00 00 00 02 0a 0b 40 2c 01 50 0b 0c 7e 11 e0 01
+40 80 00 0a af 77 00 3c 03 40 0a bd de 80 00 09 80 00 00 00 20 00 02 bd 7d 00 00 00 20 2a f7 20 00 40 24 84 91 d5 df 89 00 14
+00 00 08 60 a0 60 23 c0 00 00 97 c0 97 08 0c 00 00 00 00 00 80 80 17 c2 9d 00 00 40 02 a7 3b 80 bc 00 04 0b 0c 6d 10 81 80
+00 00 00 02 af 52 96 04 00 00 08 14 cb 00 10 00 00 00 00 01 00 00 00 15 fd 00 a0 00 10 22 5b 38 e0 40 50 01 91 c7 f9 99 00 00
+00 00
+62 00 3f
+72 00 80
+11 00
+82 00 00
+01 03
+00 00 00 00 c0 00 00 e4
+00 00 00 00 c6 66 00 e4
+00 00 00 00 cc cd 00 e4
+00 00 00 00 d3 33 00 e4
+00 00 00 00 d9 9a 00 e4
+00 00 00 00 e0 00 00 e4
+00 00 00 00 e6 66 00 e4
+00 00 00 00 ec cd 00 e4
+00 00 00 00 f3 33 00 e4
+00 00 00 00 f9 9a 00 e4
+00 00 00 00 00 00 00 e5
+00 00 00 00 06 66 00 e5
+00 00 00 00 0c cd 00 e5
+00 00 00 00 13 33 00 e5
+00 00 00 00 19 9a 00 e5
+00 00 00 00 20 00 00 e5
+00 00 00 00 26 66 00 e5
+00 00 00 00 2c cd 00 e5
+00 00 00 00 33 33 00 e5
+00 00 00 00 39 9a 00 e5
+00 00 00 00 40 00 00 e5
+00 00 00 00 46 66 00 e5
+00 00 00 00 4c cd 00 e5
+00 00 00 00 53 33 00 e5
+00 00 00 00 59 9a 00 e5
+00 00 00 00 60 00 00 e5
+00 00 00 00 66 66 00 e5
+00 00 00 00 6c cd 00 e5
+00 00 00 00 73 33 00 e5
+00 00 00 00 79 9a 00 e5
+00 00 00 00 80 00 00 e5
+00 00 00 00 86 66 00 e5
+00 00 00 00 8c cd 00 e5
+00 00 00 00 93 33 00 e5
+00 00 00 00 99 9a 00 e5
+00 00 00 00 a0 00 00 e5
+00 00 00 00 a6 66 00 e5
+00 00 00 00 ac cd 00 e5
+00 00 00 00 b3 33 00 e5
+00 00 00 00 b9 9a 00 e5
+00 00 00 00 c0 00 00 e5
+00 00 00 00 c6 66 00 e5
+00 00 00 00 cc cd 00 e5
+00 00 00 00 d3 33 00 e5
+00 00 00 00 d9 9a 00 e5
+00 00 00 00 e0 00 00 e5
+00 00 00 00 e6 66 00 e5
+00 00 00 00 ec cd 00 e5
+00 00 00 00 f3 33 00 e5
+00 00 00 00 f9 9a 00 e5
+00 00 00 00 00 00 00 e6
+00 00 00 00 06 66 00 e6
+00 00 00 00 0c cd 00 e6
+00 00 00 00 13 33 00 e6
+00 00 00 00 19 9a 00 e6
+00 00 00 00 20 00 00 e6
+00 00 00 00 26 66 00 e6
+00 00 00 00 2c cd 00 e6
+00 00 00 00 33 33 00 e6
+00 00 00 00 39 9a 00 e6
+00 00 00 00 40 00 00 e6
+00 00 00 00 46 66 00 e6
+00 00 00 00 4c cd 00 e6
+00 00 00 00 53 33 00 e6
+00 00 00 00 59 9a 00 e6
+00 00 00 00 60 00 00 e6
+00 00 00 00 66 66 00 e6
+00 00 00 00 6c cd 00 e6
+00 00 00 00 73 33 00 e6
+00 00 00 00 79 9a 00 e6
+00 00 00 00 80 00 00 e6
+00 00 00 00 86 66 00 e6
+00 00 00 00 8c cd 00 e6
+00 00 00 00 93 33 00 e6
+00 00 00 00 99 9a 00 e6
+00 00 00 00 a0 00 00 e6
+00 00 00 00 a6 66 00 e6
+00 00 00 00 ac cd 00 e6
+00 00 00 00 b3 33 00 e6
+00 00 00 00 b9 9a 00 e6
+00 00 00 00 c0 00 00 e6
+00 00 00 00 c6 66 00 e6
+00 00 00 00 cc cd 00 e6
+00 00 00 00 d3 33 00 e6
+00 00 00 00 d9 9a 00 e6
+00 00 00 00 e0 00 00 e6
+00 00 00 00 e6 66 00 e6
+00 00 00 00 ec cd 00 e6
+00 00 00 00 f3 33 00 e6
+00 00 00 00 f9 9a 00 e6
+00 00 00 00 00 00 00 e7
+00 00 00 00 06 66 00 e7
+00 00 00 00 0c cd 00 e7
+00 00 00 00 13 33 00 e7
+00 00 00 00 19 9a 00 e7
+00 00 00 00 20 00 00 e7
+00 00 00 00 26 66 00 e7
+00 00 00 00 2c cd 00 e7
+00 00 00 00 33 33 00 e7
+00 00 00 00 39 9a 00 e7
+00 00 00 00 40 00 00 e7
+00 00 00 00 46 66 00 e7
+00 00 00 00 4c cd 00 e7
+00 00 00 00 53 33 00 e7
+00 00 00 00 59 9a 00 e7
+00 00 00 00 60 00 00 e7
+00 00 00 00 66 66 00 e7
+00 00 00 00 6c cd 00 e7
+00 00 00 00 73 33 00 e7
+00 00 00 00 79 9a 00 e7
+00 00 00 00 80 00 00 e7
+00 00 00 00 86 66 00 e7
+00 00 00 00 8c cd 00 e7
+00 00 00 00 93 33 00 e7
+00 00 00 00 99 9a 00 e7
+00 00 00 00 a0 00 00 e7
+00 00 00 00 a6 66 00 e7
+00 00 00 00 ac cd 00 e7
+00 00 00 00 b3 33 00 e7
+00 00 00 00 b9 9a 00 e7
+00 00 00 00 c0 00 00 e7
+00 00 00 00 c6 66 00 e7
+00 00 00 00 cc cd 00 e7
+00 00 00 00 d3 33 00 e7
+00 00 00 00 d9 9a 00 e7
+00 00 00 00 e0 00 00 e7
+00 00 00 00 e6 66 00 e7
+00 00 00 00 ec cd 00 e7
+00 00
+82 00 80
+01 03
+00 00 00 00 f3 33 00 e7
+00 00 00 00 f9 9a 00 e7
+00 00 00 00 00 00 00 e8
+00 00 00 00 06 66 00 e8
+00 00 00 00 0c cd 00 e8
+00 00 00 00 13 33 00 e8
+00 00 00 00 19 9a 00 e8
+00 00 00 00 20 00 00 e8
+00 00 00 00 26 66 00 e8
+00 00 00 00 2c cd 00 e8
+00 00 00 00 33 33 00 e8
+00 00 00 00 39 9a 00 e8
+00 00 00 00 40 00 00 e8
+00 00 00 00 46 66 00 e8
+00 00 00 00 4c cd 00 e8
+00 00 00 00 53 33 00 e8
+00 00 00 00 59 9a 00 e8
+00 00 00 00 60 00 00 e8
+00 00 00 00 66 66 00 e8
+00 00 00 00 6c cd 00 e8
+00 00 00 00 73 33 00 e8
+00 00 00 00 79 9a 00 e8
+00 00 00 00 80 00 00 e8
+00 00 00 00 86 66 00 e8
+00 00 00 00 8c cd 00 e8
+00 00 00 00 93 33 00 e8
+00 00 00 00 99 9a 00 e8
+00 00 00 00 a0 00 00 e8
+00 00 00 00 a6 66 00 e8
+00 00 00 00 ac cd 00 e8
+00 00 00 00 b3 33 00 e8
+00 00 00 00 b9 9a 00 e8
+00 00 00 00 c0 00 00 e8
+00 00 00 00 c6 66 00 e8
+00 00 00 00 cc cd 00 e8
+00 00 00 00 d3 33 00 e8
+00 00 00 00 d9 9a 00 e8
+00 00 00 00 e0 00 00 e8
+00 00 00 00 e6 66 00 e8
+00 00 00 00 ec cd 00 e8
+00 00 00 00 f3 33 00 e8
+00 00 00 00 f9 9a 00 e8
+00 00 00 00 00 00 00 e9
+00 00 00 00 06 66 00 e9
+00 00 00 00 0c cd 00 e9
+00 00 00 00 13 33 00 e9
+00 00 00 00 19 9a 00 e9
+00 00 00 00 20 00 00 e9
+00 00 00 00 26 66 00 e9
+00 00 00 00 2c cd 00 e9
+00 00 00 00 33 33 00 e9
+00 00 00 00 39 9a 00 e9
+00 00 00 00 40 00 00 e9
+00 00 00 00 46 66 00 e9
+00 00 00 00 4c cd 00 e9
+00 00 00 00 53 33 00 e9
+00 00 00 00 59 9a 00 e9
+00 00 00 00 60 00 00 e9
+00 00 00 00 66 66 00 e9
+00 00 00 00 6c cd 00 e9
+00 00 00 00 73 33 00 e9
+00 00 00 00 79 9a 00 e9
+00 00 00 00 80 00 00 e9
+00 00 00 00 86 66 00 e9
+00 00 00 00 8c cd 00 e9
+00 00 00 00 93 33 00 e9
+00 00 00 00 99 9a 00 e9
+00 00 00 00 a0 00 00 e9
+00 00 00 00 a6 66 00 e9
+00 00 00 00 ac cd 00 e9
+00 00 00 00 b3 33 00 e9
+00 00 00 00 b9 9a 00 e9
+00 00 00 00 c0 00 00 e9
+00 00 00 00 c6 66 00 e9
+00 00 00 00 cc cd 00 e9
+00 00 00 00 d3 33 00 e9
+00 00 00 00 d9 9a 00 e9
+00 00 00 00 e0 00 00 e9
+00 00 00 00 e6 66 00 e9
+00 00 00 00 ec cd 00 e9
+00 00 00 00 f3 33 00 e9
+00 00 00 00 f9 9a 00 e9
+00 00 00 00 00 00 00 ea
+00 00 00 00 06 66 00 ea
+00 00 00 00 0c cd 00 ea
+00 00 00 00 13 33 00 ea
+00 00 00 00 19 9a 00 ea
+00 00 00 00 20 00 00 ea
+00 00 00 00 26 66 00 ea
+00 00 00 00 2c cd 00 ea
+00 00 00 00 33 33 00 ea
+00 00 00 00 39 9a 00 ea
+00 00 00 00 40 00 00 ea
+00 00 00 00 46 66 00 ea
+00 00 00 00 4c cd 00 ea
+00 00 00 00 53 33 00 ea
+00 00 00 00 59 9a 00 ea
+00 00 00 00 60 00 00 ea
+00 00 00 00 66 66 00 ea
+00 00 00 00 6c cd 00 ea
+00 00 00 00 73 33 00 ea
+00 00 00 00 79 9a 00 ea
+00 00 00 00 80 00 00 ea
+00 00 00 00 86 66 00 ea
+00 00 00 00 8c cd 00 ea
+00 00 00 00 93 33 00 ea
+00 00 00 00 99 9a 00 ea
+00 00 00 00 a0 00 00 ea
+00 00 00 00 a6 66 00 ea
+00 00 00 00 ac cd 00 ea
+00 00 00 00 b3 33 00 ea
+00 00 00 00 b9 9a 00 ea
+00 00 00 00 c0 00 00 ea
+00 00 00 00 c6 66 00 ea
+00 00 00 00 cc cd 00 ea
+00 00 00 00 d3 33 00 ea
+00 00 00 00 d9 9a 00 ea
+00 00 00 00 e0 00 00 ea
+00 00 00 00 e6 66 00 ea
+00 00 00 00 ec cd 00 ea
+00 00 00 00 f3 33 00 ea
+00 00 00 00 f9 9a 00 ea
+00 00 00 00 00 00 00 eb
+00 00 00 00 06 66 00 eb
+00 00 00 00 0c cd 00 eb
+00 00 00 00 13 33 00 eb
+00 00 00 00 19 9a 00 eb
+00 00 00 00 20 00 00 eb
+00 00
+11 01
+82 00 00
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+82 00 80
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+11 02
+82 00 00
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+82 00 80
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+11 03
+82 00 00
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+82 00 80
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+22
+38 79
+01 06
+
diff --git a/fpga/SX1301_FPGA_NOTCH_PROG_SPECTRAL_SCAN_v31.hex b/fpga/SX1301_FPGA_NOTCH_PROG_SPECTRAL_SCAN_v31.hex new file mode 100644 index 0000000..b15c4fc --- /dev/null +++ b/fpga/SX1301_FPGA_NOTCH_PROG_SPECTRAL_SCAN_v31.hex @@ -0,0 +1,1659 @@ +ff 00
+4c 61 74 74 69 63 65 00
+69 43 45 63 75 62 65 32 20 32 30 31 35 2e 30 34 2e 32 37 34 30 39 00
+50 61 72 74 3a 20 69 43 45 34 30 4c 50 31 4b 2d 43 4d 34 39 00
+44 61 74 65 3a 20 53 65 70 20 31 20 32 30 31 36 20 31 36 3a 34 33 3a 34 37 00
+00 ff
+7e aa 99 7e
+51 00
+01 05
+92 00 20
+62 01 4b
+72 00 90
+82 00 00
+11 00
+01 01
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 05 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 03 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00
+00 00 00 00 00 04 00 00 00 00 00 00 10 00 00 00 00 00 00 40 00 00 00 00 10 00 00 00 00 00 00 40 00 00 00 00 00 81 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 18 00 00 00 00
+00 00 00 00 00 04 00 00 00 00 00 00 10 00 00 00 00 00 00 40 00 00 00 00 10 00 00 00 00 00 00 40 00 00 00 00 00 03 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 20 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00
+00 00 00 00 00 0d 7a 95 00 00 02 00 46 8b 7c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 01 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 d4 15 50 00 00 04 3f c8 12 4a 00 78 00 00 00 00 00 00 00 00 00 00 00 02 00 c0 00 00 00 00 00 00 00 00 00 00
+00 00 18 00 00 08 3b 45 00 60 03 c0 7c 99 5c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 00 78 a0 00
+00 00 00 00 00 00 f2 b8 90 00 00 06 2b 48 56 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 42 02 0a 00 00
+00 00 00 00 80 00 7b 15 00 40 00 46 26 8b 7e 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 01 00 b7 b2 90 04 00 00 00 cd 12 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 02 00 00 00 a3 3a 80 40 00 03 66 94 99 5c 00 00 00 00 00 00 00 10 02 00 00 00 00 00 00 0a 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 10 08 28 00 40 00 14 2d e8 56 48 00 00 00 00 00 00 00 00 40 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 18 2a 80 00 00 00 00 66 9b 7e 00 00 00 00 00 00 00 00 00 3c ef 50 10 00 00 00 fb bf c0 00 00 00 00 00 00 00
+00 00 20 00 00 08 30 14 00 00 00 00 23 48 12 40 00 00 00 00 00 00 00 00 03 50 50 00 40 00 00 0d 67 f8 40 00 00 00 00 00 00 00
+00 00 00 00 00 03 ba 80 00 00 00 06 74 99 5c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 00 04 00 00 00 00 00 00
+00 00 00 00 01 00 00 28 00 00 00 34 21 48 56 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00
+00 00 00 00 18 08 02 04 00 00 02 67 26 9b 7c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 33 50 80 00 00 0c 01 4d 12 40 00 00 00 00 00 00 00 04 3c 00 00 00 00 00 00 c0 00 00 00 00 00 00 00 00 00 00
+00 00 30 00 70 05 f2 80 00 c0 00 40 26 9b 74 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 03 00 00 0c 08 28 00 0c 00 02 00 6d 21 41 00 00 00 00 00 00 00 00 00 00 00 00 01 00 30 00 00 00 00 10 00 00 00 00 00 00
+00 00 00 00 81 e1 6a 95 80 00 01 42 96 aa 00 00 00 00 00 00 00 00 00 06 a5 cf 50 00 00 10 0e d2 2b c0 00 00 20 05 58 a0 60
+00 00 00 00 01 00 1c 2a 90 00 08 14 00 10 a0 00 00 00 00 00 00 00 00 00 41 60 a0 01 00 00 11 03 43 fc c0 00 01 43 a4 0f 56 98
+00 00 18 00 00 8f 7a 95 00 00 01 e0 01 ea 00 00 00 00 00 00 00 00 a1 60 36 ef 50 00 00 05 80 77 a0 00 0a 00 bc 00 00 00 00
+00 00 00 00 00 10 90 15 54 00 00 02 01 50 50 00 00 30 00 00 00 00 00 02 01 d0 50 01 00 00 10 0d 74 00 00 a0 13 60 00 00 00 00
+00 00 00 00 00 08 3b 45 00 00 09 c3 24 98 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 bb 00 44 80 00 34 29 6a 05 00
+00 00 00 00 00 00 77 b8 90 00 01 76 29 5b 00 00 00 00 00 00 00 00 00 00 20 00 00 00 00 02 30 cb 81 48 40 00 00 66 c0 00 51 00
+00 02 00 00 81 90 00 00 00 00 01 42 a4 c8 00 20 00 00 00 00 00 00 00 40 00 00 00 03 00 00 00 7b e0 48 00 0e 04 60 0a 50 00
+00 00 00 00 0d 0f 00 00 00 00 00 14 2b 40 00 01 00 00 00 00 00 00 00 80 00 00 00 00 20 00 00 0b 62 80 60 00 03 60 15 0a 00 80
+00 00 00 00 00 07 6a 80 00 00 02 00 07 af 50 00 00 00 00 00 00 00 00 00 26 ef 50 00 00 0b 0c 03 94 00 00 00 00 01 4a 05 00
+00 00 20 00 00 00 08 28 00 00 06 04 03 50 50 00 40 00 00 00 00 00 00 00 3d 40 a0 01 00 00 50 03 42 80 00 00 00 00 02 00 51 00
+00 00 00 00 98 08 7b 15 00 00 00 07 bd af 50 00 00 00 00 00 00 00 15 c2 0e c5 00 00 00 20 18 10 49 00 00 00 40 20 0a 50 20
+00 00 00 00 03 00 33 f2 90 00 04 00 70 40 a0 00 40 00 00 00 00 00 00 be 43 50 00 00 00 01 10 e3 34 00 01 00 08 04 14 0a 00 00
+00 00 00 00 18 13 ea 80 00 00 09 c0 01 ca 00 00 00 00 00 00 00 00 00 47 80 b1 20 00 00 07 9c 83 81 48 10 00 1c 00 fe a0 40
+80 00 00 00 00 08 00 28 00 20 01 42 01 50 50 00 00 00 00 00 00 00 00 1e 02 dc 21 00 00 00 f8 81 00 28 82 00 00 20 07 0f 54 00
+00 00 30 00 30 0d fa 95 00 0a 00 03 8e aa 00 00 00 50 00 00 00 00 14 60 3e e0 20 20 00 03 1e 77 bd 40 04 00 00 06 7a f5 49
+00 00 01 00 00 00 38 15 50 00 40 00 00 20 a0 08 00 02 00 00 00 00 01 84 02 c0 a1 11 00 00 00 0d 43 fc 20 c0 00 02 2d 05 04 04
+00 00 00 00 78 07 37 dc 02 00 00 00 1c ea f4 00 00 00 00 30 10 00 00 00 00 e0 50 00 00 08 00 b7 29 58 00 40 00 02 08 84 d0
+00 00 00 00 00 8b da 7f c0 10 00 00 01 d0 a0 c0 00 00 00 c5 82 00 00 00 3e 50 0a 00 00 00 90 01 41 55 00 02 20 00 1c c1 24 20
+00 00 28 00 78 08 7b 15 00 a0 00 00 00 ea a4 01 80 08 1b 78 08 00 06 00 00 c5 00 81 80 4f 80 00 00 00 06 c1 20 02 09 84 c0
+00 00 02 00 00 0b 57 32 b0 08 0c 0c 03 c0 55 c1 00 00 90 f7 01 00 a0 34 3f c0 a0 00 00 02 b0 00 00 00 00 0e 01 40 0c e1 24 00
+00 00 00 00 19 8b ba 81 00 c0 00 00 40 00 00 00 00 09 80 78 00 00 11 e0 7d ef 50 03 00 00 00 07 03 c0 04 00 00 3f 48 84 c0
+00 00 00 00 00 8b dc 3e b0 04 00 1c 38 00 00 00 00 00 98 09 81 00 00 82 2b 70 50 00 30 00 00 00 40 3c 40 c0 04 03 5e 01 24 00
+00 02 00 00 81 88 2f 91 00 00 00 00 0e 00 00 00 00 01 00 18 00 00 84 06 c1 85 00 00 00 07 00 93 bd 44 00 00 56 6f 68 84 c0
+00 00 00 00 01 00 f7 76 30 00 00 01 03 40 a5 00 00 00 00 05 82 00 00 80 6b c0 a0 00 00 00 00 01 01 40 20 00 00 02 8e 01 24 00
+00 00 00 00 98 05 e2 91 00 04 00 00 14 a5 00 04 00 08 1a f8 02 00 00 00 1d a5 00 00 40 05 81 07 94 00 00 00 00 03 68 84 c0
+00 00 20 00 05 0a b3 39 50 00 20 00 38 20 50 00 08 00 50 01 02 00 00 3c 00 30 50 20 00 08 08 e1 42 80 00 00 c0 40 1e 01 24 00
+00 00 00 00 00 e3 3b e8 00 00 00 00 1c 00 00 00 00 09 80 b0 08 00 00 00 00 a5 00 00 00 00 08 13 14 00 00 00 06 03 68 84 e0
+00 00 00 00 00 00 00 3e 80 00 00 04 03 60 a5 00 00 00 98 03 02 00 00 00 03 c0 a0 00 00 00 00 f0 41 40 00 00 00 60 0c 01 24 00
+00 00 00 00 58 17 e2 85 08 00 02 66 80 00 00 00 00 00 01 98 00 00 00 00 5c a5 02 00 08 0b 8c 03 94 00 00 00 20 03 c8 84 c8
+00 00 00 00 00 08 98 2d 50 00 00 04 6c 00 00 00 00 00 10 a3 80 00 00 00 38 30 50 00 00 00 78 01 02 80 00 00 e1 c0 1c 01 24 00
+00 00 30 00 00 f5 aa 15 00 c0 00 00 06 00 00 01 00 09 1a 78 00 00 00 00 01 85 00 01 00 50 01 17 14 00 0c 02 a6 02 68 84 e0
+00 00 02 00 00 08 da 39 50 08 00 00 03 40 a5 00 30 00 b8 ef 82 40 00 00 3f d0 a0 00 30 00 00 e0 41 40 00 40 02 60 0c 01 24 00
+00 00 00 00 98 00 32 bd 00 10 09 60 14 da 14 00 00 00 0e 38 10 00 00 06 dd 85 02 00 00 80 00 76 94 00 00 80 56 3d 48 84 d0
+00 00 00 00 01 80 10 17 d0 01 00 14 00 eb 7a 40 00 20 00 0b 00 00 00 00 6c 00 50 00 00 28 00 00 01 40 00 00 08 02 b4 01 24 40
+00 00 28 00 80 01 33 dc 00 a0 00 00 0c ea 04 09 80 09 80 18 00 00 21 60 47 cf 50 00 00 00 00 07 14 00 06 40 20 39 68 84 c0
+00 00 02 00 0f 00 56 3f c0 58 00 00 00 40 f5 c1 00 00 b8 03 01 40 00 3c 2a 70 a0 00 00 00 00 07 42 80 00 0a 01 c0 26 01 26 00
+00 00 01 00 80 00 01 99 08 00 01 46 40 a0 50 00 00 07 01 30 02 00 00 00 2c e8 01 20 00 08 00 77 94 00 0c 00 a0 39 68 84 c0
+00 00 00 09 89 00 b0 26 70 00 00 80 29 50 0a 00 00 00 00 9f 81 00 00 00 3f e0 e5 00 01 00 70 00 41 40 00 c0 11 40 36 01 25 00
+00 02 00 00 18 00 00 00 00 00 02 06 04 ab 04 20 00 07 80 b8 00 00 00 00 41 85 02 00 00 00 0e 03 14 00 10 00 80 6c 48 84 c0
+00 00 00 80 00 00 00 00 00 02 00 04 00 d0 f2 40 00 00 00 07 81 40 00 40 39 d0 a0 00 09 00 31 07 02 80 00 00 04 02 a6 01 25 00
+00 00 00 01 18 11 02 94 00 00 03 e0 05 aa 04 10 00 1f 98 18 00 00 00 07 14 e0 50 00 00 1f 81 d3 3d 40 00 20 20 00 68 84 c0
+00 00 20 80 00 08 b0 3f c0 02 00 04 03 40 fa c0 00 00 f1 83 02 00 00 00 40 20 05 00 08 00 70 fb 01 40 00 20 03 c0 36 01 24 04
+00 00 00 00 00 05 e2 85 08 00 00 00 44 af 50 00 00 09 9b 38 00 00 00 00 40 85 00 00 00 00 01 5f 34 40 80 00 80 00 c8 84 e0
+00 00 00 00 00 00 50 2d 50 00 00 00 20 00 f5 00 00 00 58 a9 02 00 00 00 25 d0 a0 10 00 00 f0 e7 81 40 80 00 e0 00 24 01 24 00
+00 00 00 00 00 0b 7a 81 00 00 00 60 34 ca 14 20 10 00 1e 38 00 00 02 00 16 e0 50 00 00 00 00 77 94 00 00 00 80 79 e8 84 e0
+00 00 00 80 00 00 54 3e b0 02 00 00 02 e0 7a 40 02 80 00 05 80 00 00 1c 00 20 05 00 00 00 90 00 01 40 00 00 14 00 36 01 24 00
+00 00 30 00 00 10 00 00 00 c0 00 00 04 aa f0 01 00 08 0e 78 00 06 00 07 81 85 00 00 01 a8 01 03 14 00 04 00 00 05 48 84 50
+00 00 02 00 00 08 00 00 00 08 00 00 00 00 af 00 30 00 71 0d 82 00 e0 00 41 c0 a0 00 02 00 f0 f7 42 80 00 40 00 03 c6 02 14 00
+00 00 80 00 01 eb b7 dc 02 00 00 00 2c aa 04 00 00 20 00 00 00 00 c0 00 0e bf f0 00 00 80 00 ba a9 40 00 00 20 03 5c f5 00
+08 00 20 00 00 00 1b 7f c4 00 00 00 02 40 fa c1 00 44 00 00 00 00 00 00 21 7b f7 10 00 00 00 01 03 d4 00 00 02 40 2d 05 00 00
+00 00 18 00 80 05 6b d4 00 a0 00 00 1d 8f 54 00 00 00 00 00 00 60 00 00 34 a3 c4 21 80 08 1e ba 8d 52 ca 00 00 00 00 00 00
+08 00 00 00 01 00 08 3d 40 08 4c 0c 28 c0 a0 c1 00 00 00 00 00 00 00 00 3e e0 3e 40 00 00 f1 07 80 c1 81 a0 02 c0 00 00 00 00
+00 00 00 00 01 8b a2 80 00 04 02 00 00 00 00 00 03 a0 00 00 00 40 01 e6 4c ea f0 11 00 01 9e 3f d4 1a 40 00 20 69 da f5 00
+00 00 00 00 00 00 bc 00 00 00 20 04 00 00 00 00 00 05 00 00 00 04 00 00 20 10 af 00 30 00 f0 05 62 e1 41 00 02 44 05 0a 00 00
+00 01 00 05 18 00 28 cd 08 00 00 60 00 00 00 00 00 00 00 00 00 00 00 07 01 ae f0 00 00 50 00 52 81 40 40 00 1c 60 cc f5 00
+00 00 00 80 20 00 04 0c d0 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 df df 00 00 02 00 00 80 14 00 14 02 c2 55 0a 00 00
+00 00 00 00 18 03 bd 8d 00 00 00 60 00 00 00 00 03 00 00 00 00 00 09 60 00 16 60 00 00 00 00 f6 30 40 00 04 3e 05 cc f5 10
+08 00 00 80 00 00 d6 30 d0 02 00 02 00 00 00 00 00 00 00 00 00 02 01 00 01 4b 99 00 08 00 00 03 fa 04 00 20 01 e3 06 0a 00 00
+00 00 00 00 19 95 69 55 00 00 00 00 01 aa 04 08 00 00 00 00 00 00 00 02 3e e0 04 20 00 01 81 7b bd 40 00 00 04 7b 6f 50 48
+80 00 00 00 07 08 f8 3c 10 00 00 00 00 c0 50 c0 00 00 00 00 00 00 00 00 02 e0 01 40 00 00 10 8f 83 fc 00 00 02 42 4d ad 14 04
+00 00 00 00 00 1b a0 15 00 00 00 60 00 00 00 00 00 20 00 00 00 00 01 60 0d d0 24 00 00 05 00 80 60 40 00 00 a4 24 ff 50 41
+08 00 00 00 00 08 bc 3e 90 02 00 00 00 00 00 00 00 01 00 00 00 00 00 00 21 5b 00 40 00 02 00 a1 3d 08 00 00 13 c7 f5 8e 24 00
+00 00 30 00 00 08 01 99 00 c8 08 00 00 00 00 00 00 00 00 00 00 c0 00 42 15 81 04 03 00 1f 08 3e 00 00 0c 00 44 07 fb e2 41
+00 00 01 00 00 00 b2 66 50 08 00 c0 00 00 00 00 00 00 00 00 00 0c 00 02 03 dc 00 40 30 06 10 cf 01 40 00 40 00 02 0d aa 06 00
+00 00 00 00 18 a3 22 bc 00 00 21 40 45 9e f0 00 00 00 00 00 00 00 01 40 0f 8a 04 00 00 40 00 d3 bc 00 00 08 26 68 5b 74 00
+00 00 00 00 00 90 0c 2b c0 00 00 1c 3c 4d ff 00 00 00 00 00 00 00 00 9c 02 e0 f5 40 00 00 00 03 c1 54 00 00 c3 c0 14 b2 40 00
+00 00 00 00 80 80 2a 80 00 62 01 c0 04 cf 70 00 00 00 00 00 00 60 00 e2 84 ca 54 00 00 00 00 07 01 40 06 00 00 02 4e a3 6a
+00 00 00 00 01 10 34 14 00 00 40 00 03 4a ff 00 00 00 00 00 00 00 00 24 03 d0 55 40 00 00 00 0d 00 28 00 00 02 40 34 00 34 10
+00 00 00 00 18 a9 f3 fc 08 40 02 00 5e af 70 00 00 00 00 00 00 c0 02 00 45 af 54 00 00 00 00 bb 8c 00 8c 00 1c 05 68 30 60
+00 00 00 00 00 90 1c 3b c0 44 00 14 34 d8 ff 00 00 00 00 00 00 0c 00 04 3e c0 a0 41 00 00 d0 a5 40 d4 00 c0 00 22 07 eb 14 04
+00 02 00 00 00 01 b7 ec 00 00 00 40 1c ea 44 30 00 00 00 00 00 00 03 42 ad aa 04 00 00 03 80 7b a9 c0 80 00 26 62 fa 51 00
+00 00 00 00 00 00 13 ff c0 10 00 02 01 e0 00 40 00 00 00 00 00 00 00 04 69 e0 fa 40 00 00 08 07 82 88 00 00 01 43 7e da 10 00
+00 00 00 00 d8 c3 27 bc 00 00 00 00 05 8a 00 10 01 00 00 00 00 00 00 00 54 aa 04 00 00 00 00 d3 a8 00 00 00 06 7e ec c0 02
+00 00 20 00 01 00 5f 7f c0 00 00 1c 00 00 a0 00 00 00 00 00 00 00 00 00 36 d0 f5 40 00 20 70 07 c0 00 00 00 00 02 7c 0c f0 00
+00 00 00 00 01 93 ab d4 00 00 00 00 05 cf d0 30 00 00 00 00 00 00 00 66 16 c0 54 00 13 09 80 37 28 00 00 00 06 02 4e a3 63
+00 00 00 00 00 0a b8 3f c0 00 00 1c 33 40 ff 00 00 00 00 00 00 00 00 00 00 30 05 40 00 80 70 9d 43 d4 00 00 00 03 34 00 34 90
+00 00 00 00 00 09 32 94 48 00 00 62 95 8f 50 00 00 00 00 00 00 00 00 67 c7 cf 54 00 00 0d 81 d3 80 00 00 00 06 39 4e a5 00
+00 00 00 00 00 00 18 12 80 10 00 24 40 60 ff 00 00 01 00 00 00 00 00 00 6e e0 a0 41 00 30 10 a3 c0 14 00 00 00 22 2f ab 50 00
+00 00 00 00 00 15 73 fc 00 40 00 00 00 00 00 00 00 00 00 00 00 40 02 00 3c ca 54 00 01 85 98 7b a1 40 84 00 00 64 1a c0 02
+00 00 00 00 00 0a b6 7f 80 0c 00 1c 00 00 00 00 00 00 00 00 00 04 00 2c 03 50 55 40 00 00 00 05 03 54 00 40 00 06 00 0c 00 00
+00 00 00 00 99 89 23 fc 00 00 02 06 97 bf 70 00 00 00 00 00 00 00 00 00 3c 88 4c 00 08 05 80 3a 95 40 00 06 14 00 1c 0f 00
+00 00 00 00 01 8a 54 3e 80 00 00 5c 00 6d ff 08 00 40 00 00 00 00 01 00 03 c0 12 52 00 00 78 01 41 40 00 00 00 03 80 00 f0 00
+00 00 18 00 00 a1 78 3c 00 00 42 00 06 e0 41 00 00 00 00 00 00 60 00 63 ae 88 4c 00 00 31 81 da a9 40 06 06 34 00 00 00 00
+00 00 00 00 07 10 7c 2b c0 00 00 04 2a 6a 00 08 00 00 00 00 00 00 00 00 02 ec 12 40 00 02 38 d3 f6 2c 00 00 03 43 00 00 00 00
+00 00 00 00 10 01 fb fc 08 00 01 40 4e 8f e0 00 00 00 00 00 00 d0 00 02 f4 98 4c 00 00 21 89 16 b9 c0 84 00 14 70 40 a5 00
+00 00 00 00 00 80 1c 3d c0 00 00 14 34 1a fe 00 00 00 00 00 00 0d 00 00 23 ed 12 50 00 05 79 91 43 88 00 40 00 06 b4 0f f0 00
+00 02 00 00 d0 00 7b c1 00 00 02 00 06 e2 40 20 00 00 00 00 00 00 00 03 ee 98 4e 00 00 05 8e 17 68 80 00 00 0e 05 f8 50 4a
+00 00 00 00 01 00 00 3c 10 00 00 1c 31 40 20 01 00 00 00 00 00 00 00 00 3e eb 12 40 00 00 09 cd 77 c4 00 00 00 22 1f 0b 04 00
+00 00 00 00 00 0f 6e c1 00 00 e0 e0 34 cf f0 00 00 00 00 00 00 10 00 60 3e 9b 7c 00 00 20 1d d6 03 00 80 00 00 00 00 00 00
+00 00 20 00 05 00 33 bc 50 00 00 02 36 60 fa 00 00 00 00 00 00 01 00 00 3f 4d de 40 00 05 01 8f 81 70 00 00 00 00 00 00 00 00
+00 00 00 00 18 0f f7 1d 00 02 00 60 4c ee 52 20 00 00 00 00 00 00 00 03 ae 98 44 80 00 01 09 32 b1 40 a0 00 06 03 4b ef 01
+00 00 00 00 03 00 53 30 90 00 0e 02 3f f0 f5 20 40 00 00 00 00 00 00 00 66 e8 12 40 00 00 01 80 83 14 00 00 00 40 06 bf f0 00
+00 00 00 00 00 05 f3 d4 40 00 00 e6 74 e0 00 10 00 00 00 00 00 00 a2 02 00 00 00 00 02 80 00 ba e1 40 00 00 96 02 0a f5 00
+00 00 00 00 05 00 30 3f c0 10 00 02 60 5e 08 00 00 00 00 00 00 00 00 04 40 00 00 00 00 00 00 01 67 54 00 00 10 40 15 0f a0 00
+00 00 10 00 39 b8 2f 80 40 00 00 00 06 ef d0 10 00 00 00 00 00 c0 02 43 b4 ca 04 00 00 00 18 9e b9 c0 0c 00 00 73 d8 a5 42
+00 00 01 00 00 0a 32 b4 00 00 00 00 03 eb ff 00 00 04 00 00 00 0c 00 04 42 70 f5 40 00 00 00 03 2a 2c 00 40 00 00 04 0a d4 80
+40 00 00 08 51 80 32 80 00 00 02 00 0d af 56 00 00 00 00 00 00 00 02 06 24 af 00 00 00 03 18 da f0 c0 60 00 00 61 e8 84 c0
+80 00 20 00 0d 8b 94 14 00 00 00 1c 00 00 f5 40 00 00 00 00 00 00 00 04 2c f0 5a 00 00 00 08 9d ff 8c 01 00 01 40 06 c1 24 20
+00 04 18 00 50 e1 62 00 08 60 02 00 0c a5 05 80 00 00 00 00 00 60 08 00 05 cf 00 01 80 20 00 7e 94 00 06 00 14 00 68 84 c0
+08 03 00 00 0d 98 10 00 00 00 00 3c 00 30 50 70 00 00 00 00 00 00 00 54 02 60 0f 00 00 21 f0 c0 c1 40 20 00 00 03 d6 c1 24 10
+60 00 30 01 79 99 eb f4 08 00 08 06 00 0a 54 00 00 40 00 00 00 4a 01 47 8c e7 80 21 00 18 00 87 28 10 0d 00 80 05 49 84 c0
+83 c0 03 00 20 9f d4 3f c8 00 01 00 03 40 5a 40 00 00 00 00 00 0c 00 00 6c 50 87 00 10 0a f0 cf 41 41 00 50 15 42 06 91 24 00
+00 03 00 02 b8 a7 a9 40 00 00 00 00 04 0a 54 00 02 80 00 00 00 00 41 e0 00 0c 34 20 00 08 0f b7 a9 50 00 00 14 04 68 84 c0
+00 00 00 01 9b 1b fc 00 00 00 00 3c 38 00 a5 40 00 20 00 00 00 00 00 00 30 00 c3 41 00 30 71 a7 c1 55 00 00 00 02 16 91 24 10
+00 00 04 01 01 d5 ab 94 0c 00 02 60 40 00 00 00 10 00 00 00 00 00 01 40 20 85 50 00 01 d0 01 92 3d 50 00 c0 a6 03 e9 b7 c0
+00 00 20 40 00 18 54 30 00 00 00 0c 20 00 00 00 00 02 00 00 00 00 00 00 00 d0 aa 00 00 00 00 89 41 41 00 00 14 43 d4 9d ec 10
+44 00 30 05 00 0f b2 81 40 00 00 06 00 00 00 00 00 10 00 00 00 00 c1 c0 35 9f 70 00 00 09 9d 3f a9 50 00 0a 06 60 69 84 c0
+00 c2 82 03 20 00 94 02 98 00 00 00 00 00 00 00 00 02 00 00 00 00 00 26 00 cb ff 10 00 00 11 85 41 55 00 00 04 00 16 c1 24 04
+00 00 00 00 00 8d a7 94 00 00 01 e0 1f a0 54 10 00 00 00 00 00 00 03 e0 2e ee 02 00 00 00 01 f7 3d 50 00 4c 00 01 69 84 c1
+08 02 c0 30 00 18 76 70 00 00 00 00 00 f0 af 40 00 00 00 00 00 00 0c 3e 28 4a f0 00 00 20 00 ff 82 81 00 02 00 00 06 91 24 00
+40 00 10 00 18 a7 aa 80 00 40 00 00 7c ca 54 04 00 00 00 00 00 44 00 07 be e0 21 25 00 08 1e bf a9 50 2c 00 3e 02 69 b7 c1
+00 00 03 00 00 00 fc 3c 00 04 00 00 22 c0 aa 40 00 00 00 00 00 0c 00 00 43 60 00 00 10 00 f0 05 c1 55 00 ca 00 42 06 dd ec 00
+00 00
+11 01
+01 01
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 04 00 00 00 00 00 00 10 00 00 00 00 00 05 40 00 00 00 00 10 00 00 00 00 00 00 40 00 00 00 00 0d 01 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 00
+00 00 00 00 00 04 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 10 00 00 83 42 81 00 c0 00 22 17 05 04 00 08 00 00 00 00 c0 00 02 2f 0a a4 03 00 00 8a 34 28 10 0c 00 3c 29 d0 a0 40
+00 00 01 00 05 81 7e bd 50 0c 00 1e 04 da f5 40 01 00 00 00 00 04 00 16 07 4c a5 40 30 00 71 b1 eb d5 00 c0 03 e7 f7 af 54 00
+00 00 00 00 00 05 41 41 00 00 00 02 35 05 04 00 00 00 00 00 00 00 01 c2 2d 0a 04 00 00 00 08 b8 2a 90 00 00 00 23 d0 a0 40
+00 00 00 00 01 80 b7 bd 50 00 00 06 04 5e f5 40 01 00 00 00 00 00 00 26 05 de f5 40 00 00 18 1f 32 95 00 00 00 00 75 af 54 00
+00 00 00 00 00 03 02 81 00 00 00 00 3e 05 04 00 00 00 00 00 00 00 00 00 0d 0a a6 00 00 00 08 30 28 10 00 00 00 01 e0 a0 40
+00 00 00 00 00 00 76 bd 50 00 00 00 02 da f5 40 00 02 00 00 00 00 00 06 63 cc a5 40 00 00 00 17 6b d5 00 00 00 00 2d af 54 00
+00 02 00 00 00 8d 01 41 00 02 00 02 ae 0a 06 00 00 00 00 00 00 00 00 02 ee 0a 04 00 00 01 08 10 14 10 00 00 00 3b e0 a0 40
+00 00 00 00 07 81 b6 bd 50 00 40 00 01 7a f5 40 00 00 00 00 00 00 00 00 00 fa f5 40 00 00 f8 13 6b d5 00 00 00 00 75 af 54 00
+00 00 00 00 10 0f 71 c9 00 00 00 43 7f 05 04 00 00 00 00 00 00 00 00 40 25 0a a4 00 00 01 00 3c 28 14 00 00 04 21 70 50 40
+00 00 20 00 09 98 82 ac 50 00 00 26 64 ca f5 40 00 00 00 00 00 00 00 3e 01 ec a5 40 00 00 99 87 ab d5 00 00 02 66 7e af 54 00
+00 00 00 00 10 0b 02 a9 00 00 00 40 1d 05 04 00 10 00 00 00 00 00 20 40 2f 0a 04 00 00 00 00 b0 2a 90 00 00 04 33 f0 f5 40
+00 00 00 00 08 18 5b 29 50 00 00 20 60 4e f5 40 02 85 00 00 00 00 00 20 61 ee f5 40 00 30 01 85 b2 95 00 00 03 e6 7d ea 05 00
+00 00 00 00 d0 03 82 81 00 00 00 00 06 0a 04 00 00 00 00 00 00 00 00 00 3e 05 04 00 00 05 00 30 28 10 00 00 00 01 e0 f5 48
+00 00 01 80 08 00 33 bd 50 06 00 00 01 6e f5 40 00 00 00 00 00 06 00 00 28 ce f5 40 18 00 80 03 3b d5 00 60 00 00 24 aa 06 00
+00 00 00 00 00 0d 01 41 00 00 00 04 2e 0a 04 00 00 10 00 00 00 00 00 00 2d 0f 54 00 00 05 00 38 2a 90 00 01 40 3f 40 50 40
+00 00 00 00 00 00 32 bd 52 00 00 04 38 4a f5 40 00 00 00 00 00 00 00 00 61 58 a0 40 00 00 80 07 b2 95 00 00 00 00 5e af 54 00
+00 00 30 00 f0 87 42 81 00 c0 00 02 17 0a 04 00 00 00 00 00 00 40 00 02 05 0a 04 01 00 0f 08 97 9c 90 0c 00 00 20 f0 f5 40
+00 00 01 00 01 81 9e bd 50 0c 00 04 07 5a f5 40 00 00 00 00 00 0c 00 00 05 da f5 40 10 00 00 18 6e c5 00 c0 00 40 56 aa 04 00
+00 00 00 00 00 87 41 41 00 00 00 02 35 05 04 00 23 80 00 00 00 00 01 c2 35 a7 24 00 00 00 08 74 14 10 00 00 00 20 d0 a0 40
+00 00 00 00 01 81 7f bd 50 00 0c 06 07 7e f5 40 00 00 00 00 00 00 00 26 04 1e b1 40 00 00 18 17 fb d5 00 00 00 60 77 ef 54 00
+00 00 00 00 00 07 43 d5 00 00 00 00 14 0a 04 00 00 00 00 00 00 00 00 40 14 05 04 00 00 01 00 70 28 10 00 00 3c 00 c0 a0 40
+00 00 00 00 00 00 fa a8 10 00 00 00 02 5a f5 40 00 00 00 00 00 00 00 00 01 5a f5 40 00 00 80 0b eb d5 00 00 02 00 05 af 54 00
+00 02 00 00 00 0d 01 41 00 00 00 00 06 0a a6 00 00 00 00 00 00 00 00 03 96 0a 04 00 00 00 00 38 2a 90 00 00 02 00 d0 f5 40
+00 00 00 00 00 00 fe bd 50 00 00 00 03 fa a5 40 00 00 00 00 00 00 00 00 05 fa f5 40 00 00 00 0f fa 95 00 00 01 e0 37 aa 06 00
+00 00 00 00 10 0f 79 c9 00 00 02 40 17 0a 04 00 00 00 00 00 00 00 00 40 07 0a 04 00 00 00 00 74 28 10 00 00 04 38 e0 f5 40
+00 00 20 00 0f 18 82 ec 50 00 00 06 62 6a f5 40 00 30 00 00 00 00 00 26 60 ca f5 40 00 00 19 8d ab d5 00 00 02 66 54 ea 04 00
+00 00 00 00 00 07 41 41 00 00 00 00 1d 05 04 00 01 00 00 00 00 00 00 60 15 a7 24 00 00 01 00 d4 14 10 00 00 04 01 50 50 40
+00 00 00 00 00 18 fb bd 50 00 00 00 61 6e f5 40 00 10 00 00 00 00 00 3e 62 0e b1 40 00 00 81 87 bb d5 00 00 02 06 2c ef 54 00
+00 00 00 00 50 05 39 c9 00 00 00 43 96 0a 04 00 00 00 00 00 00 00 00 00 14 05 04 00 00 0b 1b 78 28 10 00 00 00 03 c0 50 40
+00 00 01 80 00 00 83 ec 50 06 00 20 02 4e f5 40 00 00 00 00 00 06 00 00 03 4e f5 40 18 00 80 a9 bb d5 00 60 00 00 2e ef 54 00
+00 00 00 00 10 01 82 a9 00 00 00 42 86 0a a4 00 04 00 00 00 00 00 00 20 14 0a 04 00 00 00 00 d0 14 10 00 00 14 00 d0 f5 40
+40 00 00 00 08 00 77 a9 50 00 00 20 01 da a5 40 00 00 00 00 00 00 00 1e 01 ca f5 40 00 00 00 0f ab d5 00 00 00 00 34 ea 06 00
+00 00 10 00 00 03 42 81 00 c0 00 20 25 87 24 00 00 00 00 00 00 c0 00 00 00 00 00 01 00 00 18 bc 2a 90 04 00 00 62 f0 f5 40
+00 00 03 00 00 00 5e bd 50 0c 00 1c 02 1b b1 40 00 00 00 00 00 04 00 06 00 00 00 00 30 00 01 9f f2 95 00 40 00 07 56 ca 04 00
+00 00 00 00 10 0b 02 a9 00 02 00 02 2c 0a a4 00 00 00 00 00 00 00 00 00 25 d5 34 00 00 00 08 bc 28 10 00 20 00 60 f0 55 48
+00 00 00 00 09 80 bb 29 50 00 40 06 07 58 a5 40 02 80 00 00 00 00 00 00 00 09 a3 40 00 00 00 1b fb d5 00 00 00 07 7e ca 54 00
+00 00 00 00 30 83 02 81 00 00 00 02 06 0a 04 00 00 00 00 00 00 00 00 42 25 0f 54 00 00 03 0f 3c 2a 90 00 00 04 02 e0 a0 41
+00 00 00 00 0d 01 16 bd 50 00 00 00 06 fa f5 40 00 00 00 00 00 00 00 20 05 ea a0 40 00 00 99 89 72 95 00 00 02 00 15 af 54 00
+00 02 00 00 18 87 01 41 00 00 00 00 14 05 04 00 20 00 00 00 00 00 00 02 0d dc a4 00 00 00 00 b0 28 10 40 00 01 7b d0 55 40
+00 00 00 00 0f 01 36 bd 50 00 00 00 02 7a f5 40 00 00 00 00 00 00 00 00 06 09 c5 40 00 00 18 0b 6b d5 00 00 00 07 96 ca 54 00
+00 00 00 00 10 03 c2 81 00 00 00 40 05 0a 04 10 00 00 00 00 00 00 10 20 27 0f 54 00 00 01 00 fc 14 10 10 00 04 00 5d 72 40
+00 00 20 00 09 98 3a bd 50 00 00 26 62 6a f5 40 00 00 00 00 00 00 00 1c 01 4c a0 40 00 00 80 0b ab d5 00 00 02 66 20 ab 14 00
+00 00 00 00 d0 0b 02 a9 00 00 00 40 1d 05 04 00 00 40 00 00 00 00 02 c3 4d d5 34 00 00 01 00 b4 28 12 00 00 04 03 70 50 40
+00 00 00 00 00 18 73 29 50 00 00 20 60 6e f5 40 00 02 00 00 00 00 00 20 62 08 a3 40 00 00 81 89 3b d5 00 02 02 06 2c ef 54 00
+00 00 00 00 00 03 82 81 00 00 01 60 04 0a 04 00 00 00 00 00 00 00 00 00 36 0e 60 00 00 00 00 34 2a 90 00 00 24 00 00 00 00
+00 00 01 80 00 00 53 bd 50 06 00 04 6a 4e f5 40 00 30 00 00 00 06 00 00 00 fc c1 02 18 00 58 01 72 95 00 60 01 60 00 00 00 00
+00 00 00 00 50 03 02 a9 00 00 01 c0 34 05 04 00 01 00 00 00 00 00 00 06 ee bd 00 00 00 0d 8b b0 28 10 00 00 00 03 d0 55 40
+00 00 00 00 00 00 53 29 50 00 00 00 02 6a f5 40 00 00 00 00 00 00 00 00 79 4a d5 00 00 00 38 09 ab d5 00 00 00 00 34 ca 54 00
+00 00 10 00 f0 07 42 81 00 c0 00 62 1f 0a 04 00 00 00 00 00 00 40 00 02 3d 87 24 00 00 00 80 00 00 00 0c 00 02 02 d0 fa 40
+00 00 03 00 05 80 fe bd 50 0c 00 24 04 7a f5 41 00 00 00 00 00 0c 00 06 06 1a b1 40 00 00 51 e0 00 00 00 c0 00 40 2f ca 04 10
+00 00 00 00 00 8b 02 a9 00 00 02 42 2d 05 04 00 20 00 00 00 00 00 01 40 1d 05 04 00 00 00 00 54 39 80 00 00 00 62 d0 fa 40
+00 00 00 00 01 81 df a9 50 00 00 06 05 de f5 40 00 00 00 00 00 00 00 06 02 5e f5 40 00 00 19 eb 2b 04 0c 00 00 02 6c 8a 04 00
+00 00 00 00 78 80 ba c5 00 00 00 40 24 05 04 00 00 00 00 00 00 00 02 40 14 87 24 00 00 00 00 b0 3e 80 00 00 3c 22 70 c9 00
+00 00 00 00 03 81 3e ec 50 00 00 00 02 fa f5 40 00 00 00 00 00 00 00 00 02 1e b1 60 00 00 78 8d ba 04 08 00 02 06 4f ac 50 20
+00 02 08 00 00 01 02 a9 00 20 00 02 f4 05 04 00 02 80 00 00 00 20 00 02 24 05 04 00 80 00 80 d8 32 40 02 00 02 03 db 98 00
+00 00 00 00 00 00 bf a9 50 00 00 00 01 de f5 40 00 00 00 00 00 00 00 00 06 5a f5 40 00 00 50 89 f3 14 08 00 01 e0 17 de 50 00
+00 00 00 00 10 07 42 81 00 00 03 40 3d 05 04 00 00 00 00 00 00 00 00 43 86 0f 54 00 00 00 8a bf 26 00 00 00 34 02 48 ca 40
+00 00 00 00 09 98 da bd 50 00 00 06 60 ea f5 40 00 30 00 00 00 00 00 20 64 6e a0 60 00 00 71 b5 67 94 00 00 00 07 01 cc 54 00
+80 00 00 00 00 0d 41 41 00 00 00 40 0d 0a 04 00 00 40 00 00 00 00 00 00 2c 0f a4 00 00 05 18 13 a8 80 00 00 00 2c f0 a0 40
+00 00 00 00 00 18 fb bd 50 00 00 20 62 4e f5 40 00 10 00 00 00 00 20 00 00 4e a0 40 00 38 80 b5 76 9c 00 00 00 00 34 ef 54 00
+80 00 00 00 10 0f 39 c9 00 00 00 02 bc 05 04 10 00 00 00 00 00 00 00 07 c5 0f 54 00 00 05 1b 38 39 80 00 80 00 03 c8 53 40
+00 00 01 80 08 00 03 ec 50 06 00 00 2a ee f5 40 00 00 00 00 00 06 00 00 75 cc a0 40 00 00 80 af 63 04 08 60 00 00 01 ca 34 00
+10 02 00 00 10 01 02 a9 00 00 00 00 0c 0f 54 00 04 00 00 00 00 10 00 20 2f ed 00 00 40 0d 09 9b e2 80 00 00 00 03 db 98 00
+00 00 04 00 0d 00 17 a9 50 00 00 16 03 5a a0 40 00 00 00 00 00 00 00 1e 01 78 d5 00 04 00 00 05 f7 94 00 00 e0 66 04 fe 50 00
+00 00 11 00 00 00 00 00 00 c0 00 02 2c 0a a4 00 00 00 00 00 00 40 02 42 07 05 04 01 20 07 08 b4 3e 90 04 00 2e 3b d0 a0 40
+00 00 01 20 01 80 00 00 00 04 00 00 07 4c a5 40 00 00 00 00 00 04 00 00 06 7a f5 40 10 00 51 bb 22 81 00 c0 02 c0 77 af 56 00
+00 00 00 00 00 00 00 00 00 20 00 02 0d 0a 04 00 00 00 00 00 00 00 00 02 25 0a 04 00 00 00 08 dc 15 50 00 00 00 20 00 00 00
+00 00 00 00 01 80 00 00 00 00 00 06 04 7e f5 40 00 00 00 00 00 00 00 06 06 fe f5 60 00 00 59 ef b2 95 00 00 00 00 40 00 00 00
+00 00 00 00 08 03 82 81 00 04 00 00 0c 0a a4 00 00 00 00 00 00 00 00 00 0d 0a a4 00 00 00 00 10 39 88 00 00 3d 3b e0 a0 48
+00 00 00 00 17 00 d7 bd 50 00 20 06 62 4c a5 40 00 01 00 00 00 00 00 06 03 d8 a5 40 00 00 01 87 bb 04 08 00 02 00 75 af 54 00
+00 02 08 04 00 00 00 00 00 20 03 c0 0e 0a 04 00 10 10 00 00 00 20 a0 00 26 0a 05 14 80 07 18 50 39 c0 02 00 00 00 60 fa 40
+00 00 00 00 20 00 00 00 00 00 00 20 01 da f5 41 00 80 00 00 00 00 00 00 02 7a f5 40 00 00 98 e5 b2 80 08 00 00 00 3f ea 04 00
+00 00 00 18 08 00 00 00 00 20 00 60 00 00 00 00 00 40 00 00 00 00 00 43 df 05 04 00 01 07 00 fc 39 80 00 00 00 03 f0 50 40
+00 00 20 00 07 00 00 00 00 00 00 3e 00 00 00 00 00 02 00 00 00 00 00 20 62 6a f5 40 00 00 81 83 7b 04 08 00 00 66 0e af 54 00
+00 00 00 00 01 00 00 00 00 00 00 40 05 c7 24 00 00 00 00 00 00 00 00 40 35 97 24 00 00 00 80 f6 22 80 00 00 00 00 70 50 40
+00 00 00 00 00 0a 00 00 00 00 00 34 02 0e b1 40 01 00 00 00 00 00 00 20 60 0f b1 40 00 00 79 a7 bb 94 00 02 00 06 3e ef 54 00
+00 00 00 00 30 03 03 d5 00 00 03 c0 14 05 04 00 00 00 00 00 00 00 03 c3 87 0a a4 00 01 03 00 fc 39 80 00 00 04 01 d0 c9 00
+00 00 01 80 08 00 f6 a8 10 06 00 16 00 ce f5 40 00 00 00 00 00 06 00 34 2d 78 a5 40 18 00 80 05 23 04 08 60 02 00 26 8c 50 20
+00 00 00 00 00 00 00 00 00 00 03 40 0c 0a 04 00 00 00 00 00 00 00 00 00 26 0a 04 04 00 15 8a fe e2 80 00 00 00 39 f9 98 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 ea f5 40 00 00 00 00 00 00 00 06 03 6a f5 40 03 80 f8 17 bf 94 00 00 00 06 24 ee 50 00
+00 00 30 00 00 00 00 00 00 c0 02 42 00 00 00 00 00 00 00 00 00 40 00 00 04 0f 54 03 00 00 00 94 07 50 0c 00 00 21 40 1d 40
+04 00 01 00 00 00 00 00 00 0c 00 04 04 00 00 00 00 00 00 00 00 0c 00 1e 02 5e a0 40 10 00 01 c9 a8 69 08 c0 00 40 65 a1 a4 20
+80 00 00 00 00 00 00 00 00 00 00 00 15 05 04 00 00 00 00 00 00 00 00 02 00 00 00 00 00 29 00 38 03 c0 00 00 04 40 e0 0f 00
+00 00 00 00 00 00 00 00 00 00 00 06 03 fe f5 40 00 10 00 00 00 00 00 00 04 00 00 00 00 00 80 0d e3 c0 00 00 01 63 a4 ef 00 00
+08 00 00 00 00 00 00 00 00 00 00 00 1e 0a 04 00 00 00 00 00 00 00 00 44 14 05 04 00 00 09 88 7c 3e 90 00 00 14 23 c0 f3 40
+04 40 00 00 00 00 00 00 00 00 00 00 03 5a f5 40 00 00 00 00 00 00 00 00 3b 5a f5 40 00 00 98 13 ab f5 08 00 03 60 64 cf b4 20
+00 3e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 34 a7 24 00 00 00 8e 37 e2 00 00 00 14 20 d0 a1 00
+00 00 40 00 00 00 00 00 00 00 0e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1a b1 40 00 00 18 1b a6 ec 00 00 e2 00 15 ea 00 20
+00 00 00 00 50 00 00 00 00 00 00 00 1d 0a 04 00 00 00 00 00 00 00 00 40 3d 05 04 00 00 01 8a 74 28 40 00 00 04 00 50 00 00
+00 00 10 00 2f 00 00 00 00 00 00 06 03 4a f5 40 00 00 00 00 00 00 00 26 01 4a f5 40 00 00 b8 a1 f2 80 08 00 03 40 3c ea 00 00
+00 00 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 e0 17 0a 04 01 00 00 0a d4 00 00 04 00 04 00 6e 0c 40
+00 00 00 00 00 00 00 00 00 00 00 14 60 00 00 00 00 10 00 00 00 00 00 1e 60 ee f5 40 70 10 01 8b 2a 80 00 c0 02 00 1f a5 e4 00
+80 00 00 00 00 00 00 00 00 00 00 00 3c e7 24 00 00 00 00 00 00 00 00 20 00 00 00 00 00 01 00 de 87 50 00 00 00 35 70 1d 40
+00 00 01 80 00 00 00 00 00 06 00 00 02 0f b1 40 00 00 00 00 00 06 00 04 00 00 00 00 18 00 d0 a3 ac 69 00 60 01 40 6e e1 a4 20
+80 02 00 00 00 00 03 c1 80 00 00 00 00 00 00 00 04 00 00 00 00 00 00 40 00 0f 04 00 00 00 00 d3 43 10 00 00 04 00 c9 88 00
+00 00 02 00 00 00 06 bc 10 00 00 00 00 00 00 00 00 00 00 00 00 02 00 20 00 0a f0 40 00 00 00 a3 6d 79 00 00 02 02 ae fb b0 00
+00 00 10 00 08 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 40 00 02 80 00 00 01 00 00 00 d8 03 c0 04 00 14 00 00 ff 21
+00 00 03 00 01 00 00 00 00 04 80 1c 00 00 00 00 00 00 00 00 00 04 00 00 04 00 00 00 30 00 00 03 ab c0 00 50 01 40 00 0f f0 00
+00 00 00 00 00 00 00 00 00 00 00 00 15 0f 54 80 00 00 00 00 00 00 00 00 01 00 f4 00 00 00 00 54 11 1a 01 00 2c 38 d0 f0 08
+00 00 00 00 00 00 00 00 00 00 00 00 02 ca a0 40 00 00 00 00 00 00 00 14 00 1e 0f 50 00 00 00 0b 22 ed 08 10 15 42 4e e0 f0 00
+00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 02 be bc c0 00 00 03 98 94 34 80 00 00 0c 02 50 0f 00
+00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 54 2e e9 33 00 00 00 99 90 2b 84 0c 00 00 40 1c cf 00 00
+00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 40 02 d9 60 04 00 0b 0f 04 38 40 00 00 3c 28 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 01 69 96 80 00 00 f0 01 2b 84 08 00 00 07 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 03 40 b9 60 00 00 01 00 04 03 c0 00 00 1e 35 cd 88 40
+00 00 20 00 00 00 00 00 00 00 00 0c 00 00 00 00 00 04 00 00 00 00 00 14 05 c9 96 80 00 00 d8 00 70 3c 00 00 00 40 7f ab b6 00
+00 00 00 00 00 07 01 69 00 00 00 02 80 00 00 00 00 00 00 00 00 00 00 00 02 a9 60 01 10 00 0a d3 5a 40 0c 00 14 74 6c ff 00
+00 00 00 00 00 00 80 29 50 00 00 00 04 00 00 00 00 00 00 00 00 00 00 14 00 e8 96 80 30 00 59 b0 6e 58 00 42 00 02 54 bf 70 00
+00 00 00 00 00 00 00 00 00 00 02 40 00 0f 04 00 00 00 00 00 00 00 0a 40 00 99 60 00 00 05 00 30 25 82 00 00 a4 39 f0 ff 00
+00 00 01 80 00 00 00 00 00 06 00 00 00 1e f0 40 00 00 00 00 00 06 00 40 28 c9 96 80 18 00 00 0b 60 f0 08 60 14 00 06 8f 70 a0
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 a9 60 00 00 00 00 b8 03 c0 00 00 0c 23 d0 ff 00
+00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 40 00 00 e1 96 80 00 00 59 8b f3 c0 00 00 02 06 57 af 50 00
+00 28 30 00 10 09 80 49 00 80 00 00 01 0f 0c 08 00 00 00 00 00 4a 00 02 a6 b1 d4 00 00 81 08 12 a7 c0 04 00 00 00 00 00 00
+04 00 03 00 0f 00 da 21 10 0c 00 00 70 0c f0 40 80 00 00 00 00 0c 40 00 6d 78 25 40 00 00 f0 0f e3 f0 00 c0 00 00 00 00 00 00
+80 00 00 00 10 01 80 49 00 00 00 00 02 0f 50 00 00 00 00 00 00 00 01 40 2e a3 c0 00 00 03 8e 90 1b 00 00 00 00 00 00 00 00
+00 00 00 00 09 80 5a 21 30 00 00 00 00 ea f5 08 00 00 00 00 00 00 00 36 02 fc 96 00 00 00 70 0b fa 4c 08 00 00 00 00 00 00 00
+0c 00 00 00 00 09 00 49 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 42 9c 0f 34 00 00 00 08 10 03 00 00 86 24 00 00 00 00
+04 c0 00 00 00 00 da 21 30 00 01 40 60 00 00 00 00 00 00 00 00 00 00 06 61 6c ff 42 00 10 70 09 79 20 0c 04 00 00 00 00 00 00
+00 02 00 00 00 e1 00 49 00 00 00 02 c0 00 00 00 00 00 00 00 00 00 00 42 bf 03 00 00 00 20 10 b8 39 80 00 40 00 00 00 00 00
+00 00 00 00 00 00 7a 21 30 00 00 00 04 00 00 00 00 10 00 00 00 00 00 20 07 ea 70 02 00 00 01 cb 3b 04 08 08 00 00 00 00 00 00
+00 00 00 00 00 89 80 49 20 00 00 03 83 05 00 10 00 00 00 00 00 10 01 44 15 0c c0 00 00 20 0e d7 e2 80 00 00 00 40 00 00 00
+00 00 10 00 01 99 d2 21 30 00 00 00 71 78 50 00 00 00 00 00 00 00 00 06 23 cc 33 02 00 01 04 1f 77 94 00 00 00 02 80 00 00 00
+00 00 10 00 00 81 80 49 00 00 02 c3 00 00 00 00 00 20 00 00 00 40 00 02 ae aa 50 00 00 0d 00 76 4c 00 0c 01 40 78 00 00 00
+02 00 03 00 00 19 52 21 30 00 00 20 04 00 00 00 00 01 00 00 00 04 00 06 02 fc d2 00 00 00 00 0f bd e0 00 40 08 03 80 00 00 00
+80 00 00 00 01 09 00 49 00 80 00 03 40 00 00 00 00 00 00 00 00 00 00 40 3c 0c b4 00 20 01 0a 00 3d 40 00 00 00 00 00 00 00
+08 00 01 80 00 0e f2 21 30 0a 00 00 04 00 00 00 00 00 00 00 00 06 00 20 02 18 c7 42 00 00 d8 17 e3 d4 00 60 00 00 00 00 00 00
+10 42 00 00 f0 01 00 49 08 00 00 00 3f 05 a0 00 04 00 00 00 00 00 00 03 c3 05 00 00 00 0d 00 70 15 40 00 00 04 03 68 ed c4
+00 07 04 00 00 00 72 21 30 08 00 00 03 5a 05 20 00 00 00 00 00 00 00 00 65 48 50 00 00 00 00 08 7a a8 00 00 02 00 34 8b 74 00
+00 00
+11 02
+01 01
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 08 00 00 00 00 00 00 20 00 00 00 00 08 00 00 00 00 00 00 20 00 00 00 00 00 00 80 00 00 00 00 00 02 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 08 00 00 00 00 00 00 20 00 00 00 00 08 0e 00 00 00 00 00 20 00 00 00 00 00 00 80 00 00 00 00 00 03 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00
+00 00 00 e8 b1 fc 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 29 fe d8 00 00 02 01 69 17 00 24 00 00 00 29 ee 00 00 00
+00 00 00 02 c3 3a 40 38 00 00 00 00 00 00 90 00 00 00 00 00 08 00 03 ff 7d 50 90 00 04 06 90 40 06 00 00 20 02 82 00 09 00 00
+00 00 00 00 00 01 40 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 02 01 69 17 00 00 00 00 00 00 00 00 0a 00
+00 00 00 00 00 00 36 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 06 90 00 00 00 00 00 00 00 00 00 40 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0e 1f a8 00 00 00 01 69 12 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 41 f8 70 00 00 00 06 90 00 00 00 00 00 00 00 00 00 00 00
+00 02 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00 00 00 07 00 1c 00 01 69 13 80 68 00 80 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 90 04 00 00 00 00 00 00 00 00 00 00 70 00 00 00 06 90 40 00 00 08 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07 00 00 01 01 69 13 8e 00 00 00 02 94 fe 10 00 00
+00 00 20 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d0 00 00 00 06 90 40 00 00 00 00 01 40 8f 80 00 00
+00 00 00 00 00 00 00 00 00 00 21 5e 90 00 40 00 00 00 00 00 00 00 00 00 09 00 00 02 01 21 13 9c 00 00 01 03 ac dc 81 c0 00
+00 00 00 00 00 00 00 00 00 00 02 2f cb 00 08 00 00 00 00 00 00 00 00 00 00 18 00 00 00 04 80 38 03 40 00 00 3e c3 f1 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 2b ce a0 0e 08 00 00 69 13 0e 7c 00 00 00 14 ec 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 70 00 00 00 00 00 00 00 00 03 bd f8 00 00 00 20 06 90 41 e6 80 00 00 01 41 09 0b 00 00
+00 00 00 00 00 00 07 00 03 00 00 00 00 1c 00 00 00 00 00 00 00 10 12 8c 80 00 00 01 00 a5 da 00 00 00 00 00 00 00 80 00 00
+00 00 00 00 00 00 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 02 01 28 30 70 00 00 00 02 5b 34 00 00 00 00 00 00 03 0f 00 00
+00 00 00 aa db aa 00 00 20 00 02 2f f7 98 00 00 00 00 00 00 00 00 00 96 bd 0b 00 01 08 00 3b 14 28 00 04 2a bc cb 00 c0 80
+00 00 00 0a 85 bb bc 3c 01 00 20 00 4e 08 00 00 00 00 00 00 00 00 00 95 fa 59 e1 00 00 84 01 78 26 c3 00 00 82 80 d0 0b 00 00
+00 00 00 0b d0 04 00 40 04 00 3b d7 f0 00 00 00 00 00 00 00 00 00 29 40 85 0a 00 00 00 00 00 00 00 01 00 2a bc c8 00 08 09
+00 00 00 00 7e f2 c0 3c 05 40 03 fe 4e 70 90 40 00 00 00 00 00 00 00 00 08 09 00 c8 00 00 00 00 00 01 22 01 82 82 f0 00 49 14
+00 00 04 2a d3 b8 03 c8 00 00 2b c4 10 00 01 40 00 00 00 00 00 00 12 96 d3 80 00 00 00 af 32 06 22 e0 00 0a bd cf 18 80 00
+00 00 00 0e 4a 0b 40 20 00 00 21 7c 2e 00 00 00 00 00 00 00 00 00 41 68 da 58 00 00 20 0a f0 80 00 00 00 00 81 42 c0 0b 00 00
+00 02 00 00 00 00 06 00 00 00 00 00 00 0a 00 00 00 00 00 10 00 00 19 04 c8 0d a0 80 00 af 72 00 00 00 80 0a 94 ea 98 80 00
+00 00 00 00 00 00 34 00 10 00 00 00 00 00 90 48 00 00 00 02 80 00 01 60 7f f1 e4 90 00 0a f0 80 00 00 08 00 aa 81 8f 00 04 00
+00 00 10 05 f7 6a 00 00 00 00 2b de 10 00 08 00 00 00 00 00 20 55 1f c7 c9 80 00 80 00 00 93 34 28 00 02 0a bd ea 80 80 00
+00 00 20 00 5f 0c 04 00 00 00 01 7c 0e b0 00 00 00 00 00 00 00 00 01 fc 30 78 b0 10 00 44 07 ea c0 00 00 02 82 80 bb 1f 00 00
+00 00 05 20 75 f6 60 00 00 10 3b cd d0 0a 00 20 00 00 00 20 00 20 2b de f1 00 00 00 00 05 32 16 2a 00 00 0a 94 ff 00 10 14
+00 00 00 0a 1f 07 44 00 00 00 02 14 3f f0 00 00 00 00 00 01 00 00 00 14 18 00 00 c0 00 00 00 b4 00 10 00 00 aa 80 bb 0a 40 80
+00 00 00 0f 07 00 06 40 00 10 0c 16 90 00 00 80 00 00 00 00 00 00 1e 04 85 8a a0 00 40 d0 32 14 68 00 00 4a bd eb 80 00 00
+00 00 00 00 f0 00 1c 34 00 00 42 d4 0d 00 00 14 00 00 00 00 50 00 03 e0 eb 19 01 80 00 08 00 2c 00 00 50 20 82 82 e1 08 00 00
+00 00 08 00 a5 20 00 00 02 00 00 67 80 08 0c 00 00 00 00 17 00 10 30 1c 00 1e 00 02 c0 02 7f d4 28 00 00 08 15 6d 81 90 10
+80 00 00 88 0a 04 00 00 02 20 20 03 e9 10 80 00 00 00 00 00 80 00 03 00 00 00 00 00 00 45 20 74 00 10 00 a0 97 c2 bb 0d 1c 2c
+00 00 00 8e f5 f8 02 80 00 00 01 54 e0 00 00 00 00 00 00 00 00 00 10 15 dd 80 00 23 06 64 5a c0 04 40 04 00 00 fa 00 b0 00
+00 00 00 08 ff 57 40 40 00 00 00 14 10 00 00 00 00 00 00 01 0c 00 01 61 db 58 c0 00 20 a5 81 30 02 c0 10 00 14 01 ad 18 14 00
+00 00 00 a0 0b b4 00 00 05 11 00 5f f0 00 08 00 00 00 00 00 00 52 a1 54 c8 01 80 16 02 80 ba 40 3c 02 51 00 f0 00 00 10 01
+40 00 00 02 80 d6 b4 00 00 40 00 00 1f 30 00 00 00 00 00 00 02 02 1b bc 39 30 e4 01 01 6d a7 a0 07 00 44 00 0f 00 03 0d 00 14
+00 00 00 2a 50 00 62 80 00 02 15 4f b0 01 80 00 00 00 00 00 00 00 14 15 c3 9c 00 08 00 ff 5a a0 04 00 34 2a 35 c0 19 a0 00
+00 00 00 02 5a 02 c0 43 70 00 01 68 3b 00 e4 00 00 00 00 00 00 00 01 61 d8 01 b0 00 80 0a f0 e3 42 56 22 00 93 84 b0 10 40 00
+00 02 00 21 fd 2c 03 90 00 40 2b cf 8d 00 00 00 00 00 00 00 00 11 88 15 e1 0e 00 00 00 af 70 00 04 70 80 0a bd 59 18 10 c0
+00 00 00 02 0f 5b f4 6e c0 00 02 bc 30 10 04 06 00 00 00 00 00 08 4a d4 09 f0 00 00 08 05 f0 38 c3 80 08 00 81 43 af 09 00 00
+00 00 10 0a f7 a2 00 28 50 00 3b ef f8 00 40 00 00 00 00 00 00 00 14 15 c0 01 00 00 00 00 00 20 00 00 00 0a 95 7e 81 e0 14
+00 00 20 00 af 08 04 3d 02 02 03 fe 7d 70 08 00 00 00 00 00 10 00 01 61 df 70 f0 00 00 00 00 00 43 c0 00 00 aa 80 d3 10 00 80
+00 00 04 40 01 b6 00 40 08 00 06 f6 cf 80 00 80 00 00 00 28 00 10 88 07 c9 81 00 00 00 00 00 20 00 00 04 1a bc 49 00 a0 00
+00 00 00 00 04 0e 84 20 00 00 40 0e fa 70 00 10 00 00 00 01 00 0e 5a d4 29 f0 d0 00 00 00 00 01 43 40 00 00 82 82 e0 00 00 80
+00 00 00 2f 55 a4 00 20 50 00 29 55 e8 0c a0 00 00 00 00 00 00 00 0c 35 a7 18 00 00 00 00 00 00 6c 20 08 1a 94 09 00 e0 00
+00 00 00 03 0a 03 40 01 02 00 02 bc 1d 71 04 00 00 00 00 00 00 00 00 d3 de 08 00 00 00 00 00 00 02 10 00 00 99 84 b0 0e 00 00
+00 00 01 28 09 ae 00 00 02 11 17 d6 ff 9e 00 00 00 00 00 10 00 10 81 17 d1 88 00 08 00 00 00 00 04 00 20 08 29 7f 01 d0 00
+80 00 00 82 00 5b c4 00 00 30 00 b8 2d f0 01 00 00 00 00 02 00 04 19 78 1f 91 00 00 80 00 00 00 02 c0 02 00 91 ce a0 18 00 08
+00 00 00 88 07 ec 00 00 00 00 2b ce a5 1e 00 00 00 00 00 02 00 00 3b e6 e0 00 1c 04 00 87 37 46 20 70 04 2c 84 1c 00 10 00
+00 00 00 00 00 5e 00 00 00 00 00 14 28 00 d0 00 00 00 00 00 00 00 03 3e 4f 00 00 00 00 0e ff a9 46 80 00 02 92 05 a0 08 08 00
+00 00 00 25 51 aa 00 40 05 10 28 5e 80 18 00 00 00 00 00 07 00 40 00 dc 03 1d 20 14 40 e7 00 46 64 00 50 05 a4 58 00 b0 01
+40 00 00 13 55 00 0c 20 40 50 02 88 2c 01 00 00 00 00 00 00 00 02 00 0d e0 99 a4 01 00 0d b0 24 26 00 05 00 1a 41 05 0c 00 14
+00 00 10 00 55 80 66 00 00 00 01 4d 80 01 00 00 00 00 00 00 00 00 02 ac e5 8e 00 00 40 18 5e 1e 28 00 20 05 a4 58 00 aa 00
+00 00 00 00 0a 0a 80 28 70 00 00 14 20 00 b0 02 00 00 00 00 00 00 01 0a cf 79 00 00 00 02 50 28 00 00 02 00 1a 40 00 0e 00 00
+00 02 14 22 51 ac 60 40 00 00 22 cd e0 01 00 40 00 00 00 00 00 10 23 d7 9d 8a 01 80 00 af 5b 80 60 00 00 05 a4 5c 01 c0 90
+00 00 00 0a a7 0e 94 20 00 00 02 bc 5d f0 e0 08 00 00 00 00 00 00 02 28 2c 11 00 14 00 08 f1 74 06 40 00 20 1a 40 00 00 00 00
+00 00 00 25 a0 04 00 00 00 10 20 4f bf 9d 00 80 00 00 00 00 00 10 22 46 b7 89 00 00 c0 10 18 24 30 00 00 05 a4 5e 80 00 08
+00 00 20 93 a5 02 00 00 00 00 02 bc 1e b0 c0 04 00 00 00 00 00 00 03 1c 38 01 f0 00 00 42 00 a8 40 00 00 40 1a 40 03 00 01 00
+00 00 00 00 00 00 00 00 10 10 22 5d f3 1d 00 40 00 00 00 00 00 10 00 d4 00 1c 00 82 82 af 53 4c 60 00 00 05 a4 4c 50 10 00
+00 00 00 00 00 00 00 00 00 00 03 2c 0a 08 e0 08 00 00 00 00 00 00 00 0c 00 f1 90 0c 09 e4 73 f4 20 00 00 04 1a 41 00 0d 40 00
+00 00 00 20 57 a8 07 00 00 a0 3f e4 e8 18 00 00 00 00 00 00 00 10 a0 0c e3 81 40 00 00 ef 16 0e 70 00 00 05 a4 4a 01 c0 00
+00 00 00 03 5f 0a c0 40 00 00 03 7c fd 10 00 04 00 00 00 00 00 00 0b 80 3b 10 92 00 00 0f ff e8 00 00 00 04 1a 40 00 10 00 00
+00 00 08 aa d9 ee 07 40 03 20 28 1e 17 9e 40 00 00 00 00 00 00 00 90 b6 a0 00 00 2c c0 bc 1a 00 39 00 30 05 a4 58 00 00 00
+80 00 00 06 85 5f 84 6e 10 30 01 40 28 09 02 00 00 00 00 01 00 08 08 17 5d 00 01 42 a1 4b c0 00 00 08 03 04 1a 40 00 0c 00 0c
+00 00 00 20 0b b8 60 00 01 00 01 4f b3 00 14 00 00 00 00 00 00 82 00 84 a1 00 00 00 00 a7 bb 00 00 00 00 01 a4 4e 00 10 00
+00 00 00 02 02 52 00 28 00 00 02 bc 1e 00 00 00 00 00 00 00 08 02 20 01 7c d8 80 00 00 02 61 6c 40 00 00 0c 1a 40 00 0a 00 00
+00 00 00 00 57 68 00 00 04 10 29 ce b8 00 40 80 00 00 00 00 20 50 a3 5c e0 00 00 90 40 89 5e 5e 00 80 88 01 1f 59 39 9a 00
+00 00 00 00 05 00 00 34 00 50 01 44 1e f0 0b 80 00 00 00 00 05 00 0a 34 10 d0 00 15 00 0c b0 b8 02 85 00 00 20 7e 8d 9d 40 00
+00 00 04 20 a3 ba 03 80 00 10 22 5f d1 0f 00 00 00 00 00 00 00 00 01 56 a8 0a 00 00 48 81 00 5e 6d 00 00 00 07 59 d8 00 00
+00 00 00 02 42 0a 14 00 80 00 03 1c 3c 09 c0 00 00 00 00 00 00 00 01 7c 3e 30 b0 04 00 04 20 3c 27 a8 00 00 16 c6 95 00 80 00
+00 02 00 20 47 a0 03 80 00 00 00 00 01 01 01 00 00 00 00 00 00 00 23 7f 88 00 a0 00 00 af 36 d6 28 00 08 01 1e 69 01 90 00
+00 00 00 02 1c 7b c0 40 00 00 00 00 00 d8 d0 08 00 00 00 00 00 00 01 35 fa 10 00 00 00 00 a0 3f c0 00 00 00 20 64 80 1d 00 00
+00 00 00 28 0f 20 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 10 22 5e fd 80 00 00 00 af 1b 4e 70 00 42 03 fc cf 78 a0 00
+00 00 20 02 e0 16 00 20 00 40 00 00 00 00 00 00 00 00 00 00 40 00 03 1c 3c f8 f0 01 00 00 50 a8 23 e0 04 00 17 c1 ed 10 00 10
+00 00 00 60 43 a4 66 00 20 00 01 46 ab 98 00 80 00 00 00 00 00 10 20 40 17 01 00 00 40 47 3f 3e 60 02 01 00 80 dc 00 f0 14
+00 00 00 02 1c 73 80 6c 60 00 02 bc 2f f8 90 14 00 00 00 00 00 00 01 08 0d 00 90 00 00 04 70 80 43 00 40 80 00 02 d0 0f 00 00
+00 00 00 00 55 06 02 80 00 00 11 e5 bf 00 00 00 00 00 00 00 00 00 83 56 cf 18 0c 00 00 af 3a 80 01 02 00 02 bd fd 30 00 00
+00 00 00 00 0a 0a 84 78 00 00 02 06 eb 78 00 00 00 00 00 00 80 00 09 3d 7f 10 00 00 00 00 50 a4 00 28 50 00 01 40 ff 0d 00 00
+00 00 04 20 a3 21 47 40 03 02 3f de e8 1d 00 00 00 00 00 00 00 00 29 4c 99 8f 40 0c 00 00 5a e4 04 30 01 02 2d db b0 b0 04
+00 00 00 02 81 03 82 20 00 32 01 7c 1a 10 f0 00 00 00 00 00 00 02 02 a8 3b b8 93 82 c0 00 a0 2d e3 80 00 00 02 82 ad 1e 58 80
+00 00 00 00 00 00 00 00 00 04 01 5f 91 00 00 00 00 00 00 10 20 00 09 06 e0 00 00 00 00 c7 bb 94 00 40 08 0a 0d cb 00 10 10
+00 00 00 00 00 00 00 00 00 00 01 7c 3c 00 e0 00 00 00 00 00 85 00 00 50 de b0 94 00 00 08 99 63 43 80 00 00 b8 c6 90 08 00 00
+00 00 00 00 00 00 00 00 04 00 11 56 a7 8c 40 00 00 00 00 00 50 00 ab 4e f7 9f 20 10 40 89 32 5e 78 81 00 0b 96 6e 81 e0 01
+00 00 00 00 00 00 00 00 00 50 01 3e e9 00 02 08 00 00 00 00 42 00 0a 15 e8 09 f0 81 00 4c b0 b5 c4 10 00 00 a1 46 f3 10 14 14
+00 00 00 00 00 00 07 40 00 00 31 e6 ab 88 a0 00 00 00 00 00 00 00 81 5c b7 01 00 00 00 8f 56 54 69 70 04 18 1f 6f 01 c0 08
+00 00 00 00 00 00 00 70 00 00 02 26 ca f1 b4 00 00 00 00 00 00 00 09 7c 2d 08 c0 14 00 04 57 fd e0 20 00 00 87 e5 a0 18 01 40
+00 02 00 00 00 00 07 00 00 10 22 5c b5 1e 00 00 00 00 00 00 00 00 28 14 e1 9c c0 20 04 05 53 8e 01 00 00 00 00 09 98 d0 00
+00 00 00 00 00 00 00 41 00 00 03 2c 0e 08 00 02 00 00 00 00 00 00 42 80 20 00 0a 00 00 05 f0 e4 20 2d 00 20 29 40 db 0f 08 00
+00 00 00 00 00 00 00 00 00 10 20 40 07 9c 00 00 00 00 00 04 00 01 b8 e7 98 00 00 00 00 05 32 a0 39 02 02 00 28 dc 31 81 48
+00 00 20 00 00 00 00 00 00 00 01 08 0b 09 00 c0 00 00 00 00 20 00 09 0c ed f0 e0 00 00 05 f0 6a 43 a8 40 00 02 81 00 08 81 00
+00 00 00 00 00 02 00 00 00 41 2b d6 88 00 20 00 00 00 00 14 10 04 ab 6e 98 19 a1 00 40 47 33 96 e0 00 00 0a 94 de 70 18 02
+00 00 00 00 00 00 0c 00 00 00 42 bc 00 30 d1 40 00 00 00 00 02 00 0e 16 78 30 80 08 00 04 70 40 20 00 00 00 aa 80 a0 8c 00 00
+00 00 00 00 00 00 00 00 00 00 2b d4 d0 1e 00 00 00 00 00 08 10 00 3c 67 c8 19 00 00 00 a5 56 6c 70 00 00 0a bd 6d 01 80 00
+00 00 00 00 00 00 00 34 00 00 00 14 2e 01 00 00 00 00 00 00 02 00 02 88 cd 10 b0 00 00 05 50 ac 44 00 20 44 82 83 e1 00 00 00
+00 00 01 68 53 f8 60 04 22 00 01 5e af 9f 20 00 00 00 00 00 00 00 ab 77 b0 00 00 08 48 47 13 9c 03 85 04 0a 94 db 01 80 00
+c0 00 00 02 ad 5f 80 00 04 b0 00 14 30 71 e0 00 00 00 00 00 00 04 1a 14 59 00 01 40 80 04 70 c0 20 3c 00 00 aa 81 80 00 00 08
+00 00 00 00 00 03 40 00 50 00 17 ff b1 08 00 00 00 00 00 10 00 04 14 46 9f 98 00 00 00 a1 7a 5c 01 f0 00 00 00 00 01 e0 00
+00 00 00 00 00 00 06 2c 02 00 02 07 cc 11 00 00 00 00 00 00 b8 00 02 9e 68 10 00 00 20 0e 2f f2 40 06 00 00 00 00 01 10 00 00
+00 00 00 0e e3 20 00 00 00 00 37 cf 90 00 21 00 00 00 00 00 00 00 a0 c6 e3 81 0c 14 0e fa 3b 80 00 00 60 00 a3 e8 00 10 01
+80 00 00 08 bb 7a 40 00 10 04 03 94 4a 00 b4 08 00 00 00 00 00 00 4f 8c 5c 08 e0 01 08 2f a0 40 00 00 00 00 00 37 c0 0d 00 00
+00 00 00 0e ef bc 62 a0 00 00 01 4c f8 00 00 00 00 00 00 00 40 00 b0 4c c1 00 40 00 00 af 32 c0 01 30 20 00 00 00 80 01 00
+c0 00 00 00 bb d7 74 41 00 00 02 bc 2e b0 00 00 00 00 00 00 00 00 1b 2e 6f 00 00 00 00 00 a0 74 40 20 02 00 00 00 07 00 00 08
+00 02 00 0e ed 69 42 80 00 00 3e ee d8 00 00 00 00 00 00 03 00 00 00 00 07 0f 20 00 00 8b b0 00 00 00 04 00 28 5e 80 00 00
+00 00 00 00 77 b3 42 40 00 00 02 9f 4a d0 80 00 00 00 00 02 00 00 00 00 00 08 84 00 00 04 77 ac 00 00 00 00 02 82 0f 00 00 00
+00 00 00 0c c3 ad 43 80 00 00 28 56 9f 01 00 00 00 00 00 00 00 00 81 f6 d7 1f 00 00 80 05 77 20 62 80 00 02 80 ca 81 90 00
+00 00 20 00 ff f7 be 00 70 02 01 5e 5c 70 e0 00 00 00 00 00 00 00 18 7c 4c 00 f0 00 00 00 50 c0 c0 10 50 00 28 01 07 1c 04 00
+00 00 04 0e f1 29 c2 e8 00 00 2b dd d7 81 40 00 00 00 00 28 00 02 a9 cf e0 01 1c 80 10 00 03 44 04 00 01 19 00 dc 80 00 00
+00 00 00 08 ff 0f 2c 74 00 00 00 28 0d 00 e2 00 00 00 00 00 00 00 08 96 cc 00 d0 00 00 0a 50 76 63 80 00 00 92 81 81 00 00 00
+00 00 00 00 00 00 e7 c0 00 10 11 dc a9 9e 00 00 00 00 00 00 00 00 3b fc 81 1d 0c 00 00 00 00 34 01 00 00 00 15 c0 78 10 00
+00 00 00 00 00 00 1e 70 00 00 01 1c 30 11 90 00 00 00 00 00 00 00 02 fd de 08 e0 00 00 00 00 00 c0 28 00 00 02 80 90 0e 14 00
+00 00 00 0a a3 b8 07 80 20 10 23 cc f9 0e 1c 80 00 00 00 02 00 30 3b d6 b0 18 60 08 08 af 78 5c 20 00 31 28 90 cf 58 e0 00
+80 00 00 08 aa 00 00 6c 05 00 01 14 3f f9 e0 00 00 00 00 00 00 06 03 fc 3e 00 f4 02 c8 05 f0 b6 42 40 03 00 8a 01 ff 10 00 08
+00 00 00 32 11 38 00 00 00 c0 80 37 db 81 80 00 00 00 00 10 00 00 2b de b8 0a 00 00 00 05 7b c0 01 00 00 02 80 60 50 00 00
+00 00 00 42 48 03 7c 02 80 08 38 12 7d 10 f4 00 00 00 00 00 80 04 00 14 0b 11 b0 00 01 0a f0 74 00 00 00 00 14 02 c1 8e 08 00
+00 00 00 32 11 38 00 00 06 00 00 00 08 19 1c 50 00 00 00 00 00 00 a9 40 15 81 00 18 00 42 80 00 7d 00 51 09 08 fb f9 da 05
+80 00 00 02 48 03 c0 00 00 00 00 00 00 b0 d0 19 00 00 00 00 00 00 09 68 0e 08 c0 04 00 08 1b 3c 07 a0 05 02 81 42 bb 9d 51 80
+00 00 00 32 11 2a 60 28 12 06 b0 d7 c0 00 20 00 00 00 00 10 00 00 83 cc 00 0a 00 00 00 a0 76 80 02 80 00 0b 81 69 30 b0 00
+80 00 00 02 48 07 44 00 02 20 0e 2e 6d 00 00 00 00 00 00 02 80 00 08 3c 00 d1 b1 80 00 0a 00 c2 40 00 00 02 a0 3f a0 1b 08 08
+00 02 00 32 11 2a 06 00 00 80 a2 c6 d1 9d 00 00 00 00 00 00 00 81 83 c4 07 00 14 00 00 0a 18 26 7c 02 00 18 22 cb 01 d0 00
+00 00 00 02 48 07 c4 00 00 00 0b 0e dc 10 80 00 00 00 00 00 00 04 48 3c 20 00 00 00 00 00 50 3f c3 40 40 00 a1 46 b5 1b 14 00
+00 00 00 32 11 78 00 00 00 00 83 57 d1 80 00 00 00 00 00 00 00 00 29 4e e0 18 00 00 00 a0 72 80 00 02 00 08 0b fa 80 00 00
+00 00 20 02 48 03 7c 00 00 00 0d 3e c8 10 00 00 00 00 00 00 00 08 02 a8 08 00 04 00 10 0f a0 fc 00 00 40 02 86 97 85 0d 00 00
+00 00 00 32 11 68 00 00 00 00 b0 fe db 9c 01 00 00 00 00 00 00 01 29 57 e0 1e 80 00 00 00 00 14 04 00 00 48 22 7f 80 00 04
+00 00 00 02 48 03 c0 00 02 02 09 1c 7c d9 08 08 00 00 00 00 00 00 02 a8 38 01 b4 00 00 00 00 02 42 c0 00 00 a1 47 b5 0f e0 00
+00 00 01 32 11 68 06 40 00 00 81 df d0 00 21 40 00 00 00 00 00 02 29 4f 80 01 00 00 00 af 5f ae 70 00 00 08 0b 5a 59 a0 14
+00 00 00 02 48 07 7c 20 00 04 2c 7e ef 00 04 08 00 00 00 00 00 00 02 a8 1a 00 f0 00 00 00 50 7d e4 10 20 01 86 95 e0 0f 00 00
+00 00 00 32 11 78 67 c0 03 00 8b f6 cf 80 40 08 00 00 00 00 00 00 29 57 c5 01 40 09 02 af 5f 40 01 40 22 03 bb 58 f8 00 00
+c0 00 00 02 48 07 c0 60 20 3c 08 2c f9 10 0a 00 c0 00 00 00 00 00 12 a8 2b 08 f8 00 81 20 a0 a1 4b 88 03 00 1d d6 e7 80 00 08
+00 00
+11 03
+01 01
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 00 00 02 00 00 00 00 00 00 00
+00 00 00 00 01 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 08 00 00 00 00 00 00 20 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 08 00 00 00 00 00 00 20 00 00 00 00 0c 00 00 00 00 00 40 30 00 00 00 00 00 00 40 00 00 00 00 00 03 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 00 38 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 40 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 08 00 00 00 00 80 00 20 00 00 00 00 00 00 80
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 02 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 03 04 93 8e 91 00 00 0c 00 00 00 00 00 00 b2 ac c1 10 00 08 10 67 02 d4 02 80 00 09 54 3d 19 00 00
+80 00 00 00 00 00 00 70 00 30 0a 35 61 80 00 00 80 00 00 00 00 00 0b 17 e1 80 e0 00 84 08 33 a3 66 90 00 00 a9 45 f9 8c 00 0c
+00 00 00 00 00 00 00 00 00 04 82 82 81 00 00 00 00 00 00 00 00 02 81 43 e1 00 00 00 00 46 17 fc 00 00 00 09 38 6a 10 00 10
+00 00 00 00 00 00 0c 00 00 00 0a bd ec d9 80 00 00 00 00 00 00 00 0a bd fd 80 00 00 00 0c 7b 29 62 00 00 00 a3 5e 18 00 14 80
+00 00 00 09 30 a4 c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 95 41 c0 00 00 00 00 00 00 00 28 00 00 08 28 0a 00 f0 00
+00 00 00 c0 a3 3a a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 95 eb 19 80 00 00 00 00 00 00 40 00 00 ab d6 91 80 00 00
+00 02 00 01 9b a0 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 81 41 e0 00 00 00 00 00 00 3c 6c 00 02 09 38 4b 00 00 00
+00 00 00 00 a7 d2 80 00 00 00 00 00 00 01 c0 00 00 00 00 00 00 00 0a bd 69 00 00 00 00 00 00 02 c3 80 00 00 a3 56 00 00 00 00
+00 00 00 25 f0 a4 00 00 00 40 ab c3 c0 10 00 00 00 00 00 00 00 00 82 82 f0 08 00 00 00 df 06 80 00 00 00 48 28 28 00 88 00
+00 00 20 02 05 76 00 00 00 02 08 15 6a 18 80 00 00 00 00 00 00 00 0a bd 5e 00 10 00 04 0c f7 69 e0 00 00 00 ab d4 80 19 00 00
+00 00 00 00 00 01 42 80 00 00 82 82 e0 00 e0 00 00 00 00 00 00 02 81 42 e0 08 00 00 00 47 9f 24 24 00 00 08 14 38 01 08 00
+00 00 00 00 00 00 20 6c 00 00 0a bd cc 18 0c 00 00 00 00 00 00 00 0a bd db 18 10 00 00 08 5f 30 00 00 00 00 ab dd c1 9c 40 00
+00 00 00 00 00 00 03 80 00 00 81 41 c0 00 00 20 00 00 00 00 00 00 00 00 00 0c 08 00 10 67 0a dc 3c 00 00 0b 28 6f 00 00 00
+00 00 00 00 00 00 00 04 00 60 0a bd ce 50 04 01 80 00 00 00 00 00 00 00 00 50 10 01 84 08 37 f8 00 40 00 00 b1 44 00 00 00 98
+00 00 00 00 00 00 03 c0 00 00 97 c2 80 08 00 00 00 00 00 00 00 04 ab c2 e0 80 00 00 00 46 1f 40 34 00 00 00 65 ea 70 f0 04
+00 00 00 00 00 00 06 04 00 00 08 15 cc 00 11 40 00 00 00 00 00 00 08 14 6b 30 00 00 00 0c 7f a8 00 00 00 00 29 fc 88 18 00 80
+00 00 00 00 00 00 44 00 03 20 81 42 e0 0e 00 0c 00 00 00 00 00 00 97 c2 d0 90 00 0c 00 00 00 04 00 00 30 00 00 00 01 00 00
+00 00 00 00 00 00 20 70 00 30 0a bd 7f 01 80 00 80 00 00 00 00 00 08 14 ed 71 a0 00 80 00 00 02 02 00 03 00 00 00 00 1c 00 00
+00 00 01 20 a0 ac 40 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 97 c2 d1 0f 00 00 02 af 06 84 3c 00 00 03 2c 09 08 90 00
+00 00 00 02 af 7e 20 60 00 00 00 00 00 80 00 00 00 00 00 00 00 00 08 14 4f 80 00 00 00 20 57 7e 00 40 00 10 22 4e 93 81 00 00
+00 00 00 00 00 00 00 00 00 00 82 80 90 00 00 00 00 00 00 00 00 80 24 c3 91 00 00 00 00 00 00 06 00 00 00 00 64 59 b0 00 00
+00 00 00 00 00 00 00 00 00 00 0a bd e8 00 00 00 00 00 00 00 00 01 02 8d fc 98 00 00 00 00 00 00 40 00 00 00 29 d4 a0 1e 00 00
+00 02 00 00 00 00 00 00 00 00 81 41 83 00 00 00 00 00 00 00 00 00 06 7e e0 0f 00 00 02 05 06 80 00 00 00 03 2c 0a 01 00 00
+00 00 00 00 00 00 00 00 00 00 0a bd 7a 80 00 00 00 00 00 00 00 00 02 9e eb 00 00 00 00 2a f5 fc 00 00 00 10 22 5c 90 1c 1c 00
+00 00 00 24 e1 bc 00 00 00 00 90 a4 fd 18 00 00 00 00 00 00 00 00 b2 a4 80 00 00 00 0a 0a 0b c0 20 00 00 00 1c d8 d8 00 00
+00 00 20 02 8d d0 00 00 00 00 0b 9c 08 19 d0 00 00 00 00 00 00 00 0b 14 c0 00 e0 00 00 2a f5 68 07 c0 00 00 2a c6 85 1a 00 00
+00 00 00 20 a0 b8 00 00 00 00 81 42 80 00 00 00 00 00 00 00 00 00 82 82 a0 0b 00 00 02 05 0a 80 00 00 04 00 ae db 00 b0 00
+00 00 00 02 af 76 06 00 05 00 0a bd da 00 00 00 00 00 00 00 00 00 0a bd ce 00 a0 00 00 2a f7 6c 68 00 00 00 09 f5 95 9a 00 00
+00 00 00 20 50 60 00 00 00 00 00 00 07 00 00 00 00 00 00 00 00 00 b2 84 f0 20 00 00 00 00 00 00 00 00 00 01 9c 18 51 e0 00
+00 00 00 02 af 72 40 78 00 60 00 00 00 02 00 01 80 00 00 00 00 00 0b 14 c0 00 00 01 80 00 00 00 00 00 06 10 20 de ed 8a 00 00
+00 00 00 00 00 00 c0 00 00 00 06 6f 90 0e 01 00 00 00 00 00 80 00 06 7e e0 00 00 00 00 57 df 40 00 00 00 00 66 d8 50 00 14
+00 00 00 00 00 00 20 00 00 00 02 9d c9 1a 00 00 00 00 00 00 00 00 02 9f c9 19 80 00 00 0a 13 bc 00 00 00 00 29 e7 bf 1c 00 00
+00 00 00 00 00 00 00 00 00 00 19 c2 8b 00 00 80 00 00 00 00 00 00 00 00 00 00 00 08 00 af 04 2c 2c 00 00 0a a8 1c 30 00 00
+80 00 00 00 00 00 00 00 00 01 02 0d da 80 00 00 00 00 00 80 60 00 00 00 00 00 00 00 c8 0a f7 6a 00 00 00 00 a9 55 b8 00 00 2c
+00 00 08 09 30 bc 20 00 00 00 06 57 d5 00 00 00 00 00 00 00 00 00 97 c0 d0 00 00 00 10 67 0b 24 28 00 00 0a bc 1a 11 00 06
+00 00 00 40 a3 52 1e 00 00 00 02 9d f9 f0 00 00 00 00 00 00 00 00 28 14 ed 01 80 00 04 08 37 b6 07 80 00 00 81 57 a8 1a 00 a0
+00 00 00 01 9d 64 20 00 00 00 00 00 03 0d 00 00 00 00 00 00 00 81 81 41 c1 9c 00 00 00 46 bb f4 30 00 00 09 7c 2c 00 c0 02
+00 00 00 00 a7 1a de 00 00 00 00 00 00 80 10 00 00 00 00 00 00 00 0a bd 7c 98 90 00 00 0c 73 70 00 00 00 00 81 54 b0 19 00 00
+00 02 01 06 30 e8 00 00 00 00 13 cc 80 08 00 00 00 00 00 00 00 00 00 00 00 0e 00 00 00 d0 d3 9e 40 00 00 08 6d 68 11 00 00
+00 00 00 40 a3 57 40 00 00 00 01 17 49 10 00 00 00 00 00 00 02 00 00 00 00 01 e0 00 20 0e 0d 80 e2 80 00 00 89 d6 18 1a 00 00
+00 00 00 01 9d 64 00 00 00 00 17 c3 80 19 00 00 00 00 00 00 00 81 ab c1 c0 00 00 00 00 05 d2 f4 7a 00 00 08 28 2f 10 e0 00
+00 00 20 00 a7 52 54 2a 80 01 02 05 69 18 a0 00 00 00 00 00 00 00 08 14 ca 01 80 00 00 0e 79 a0 43 d0 00 00 ab d4 a9 80 00 00
+00 00 00 06 30 e4 c4 00 00 00 05 d7 85 8b 00 80 00 00 00 00 00 00 00 00 00 00 40 00 10 67 0b 34 38 00 00 09 39 68 00 00 00
+00 00 00 40 a3 52 4c 28 00 00 00 9d 4c 78 c0 14 00 00 00 00 00 00 00 00 00 18 02 00 04 08 31 7e 03 00 00 00 a3 5c 11 9e 00 00
+00 00 00 01 9d a7 42 a0 20 00 18 c3 a0 8b 00 00 00 00 00 00 00 00 81 40 c0 00 40 00 00 57 57 40 2c 00 00 02 4c 0b 00 80 00
+00 00 00 00 a7 5a 4e 00 04 01 12 8c 4b 11 c0 00 00 00 00 00 00 00 2a bd c8 00 08 01 80 0a 13 f0 00 00 00 10 28 dd 90 01 00 18
+00 00 00 00 00 03 62 80 00 00 11 4d 93 0d 00 00 00 00 00 00 00 00 97 c2 80 0f 00 00 00 4f 56 80 2c 00 00 00 2f 6d 02 00 08
+00 00 00 00 00 00 14 28 00 00 02 3c 6e 80 d0 00 00 00 00 00 00 00 18 14 ec 00 10 00 00 04 5f 28 60 00 00 00 2a de c0 00 15 40
+00 00 00 20 50 a0 00 00 02 00 97 c0 d1 10 00 0c 00 00 00 00 00 00 aa 81 c0 00 00 0c 02 0a 0b c4 40 00 30 00 00 00 08 e0 00
+00 00 00 02 af 5f 40 00 00 20 08 15 5b 81 c0 00 c0 00 00 00 00 00 0a 95 db 00 e0 00 c0 2a f5 e7 62 07 03 00 00 00 05 00 00 00
+00 00 00 00 00 00 00 00 00 00 ab c3 a0 00 00 00 00 00 00 00 00 00 ab c3 e1 00 00 00 02 05 0e 04 24 00 00 03 1c 0e d1 00 10
+00 60 00 00 00 00 00 60 00 00 08 14 ff 00 00 00 00 00 00 00 00 00 08 15 5a 81 e0 00 00 2a f7 f6 06 00 00 12 22 4c 80 08 00 00
+00 00 08 20 50 63 c3 40 00 04 05 e6 97 00 00 00 00 00 00 00 00 00 97 c2 c0 0e 00 00 02 0a 02 82 00 00 00 03 9c 0c 51 e0 00
+00 00 00 02 af 5f 40 04 00 00 00 9f 58 18 00 00 00 00 00 00 00 00 08 15 4f 01 90 00 00 2a f5 e5 40 00 00 10 01 4c 9d 8e 00 00
+00 02 00 20 a0 3b c0 00 04 00 00 00 00 00 01 10 00 00 00 00 00 00 86 de 80 00 00 10 02 05 06 00 00 00 40 02 4c 3a d0 a0 01
+00 00 00 06 af 5e 00 00 00 00 00 00 00 00 00 08 00 00 00 00 02 00 08 9d 61 00 c0 00 00 2a f5 a4 00 00 00 10 28 c5 9b 98 00 00
+00 00 00 00 00 00 02 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 82 82 f1 20 84 00 02 55 06 40 20 00 00 01 19 fd 01 00 00
+00 00 20 00 00 00 06 64 00 00 00 00 00 00 e0 00 00 00 00 00 00 00 0a bd 4e 98 00 00 00 2a 51 f8 66 40 00 00 31 ee d0 0a 00 80
+00 00 00 00 00 00 02 00 00 00 06 67 d0 80 a0 00 00 00 00 00 00 00 93 9e 85 00 00 00 02 4e 1b 80 20 00 00 00 5e df 00 a4 00
+00 00 00 00 00 00 00 04 00 00 02 9e ff 50 a0 00 00 00 00 00 00 00 0a 35 c1 98 00 00 00 28 df 00 00 40 00 00 09 d4 f0 01 80 00
+00 00 00 20 50 60 03 80 00 00 00 00 03 8b 00 00 00 00 00 00 20 00 24 c0 b0 08 00 00 02 0a 02 80 3c 00 00 02 4c 2d 58 a0 00
+00 00 00 02 af 73 40 78 00 60 00 00 00 b8 0a 01 80 00 00 00 01 01 02 8c f9 00 10 01 80 2a f7 60 00 40 06 10 28 cd 85 0a 00 00
+00 00 00 20 a0 2c 00 00 00 00 00 00 00 00 00 40 00 00 00 00 80 00 01 f5 c0 00 00 00 02 05 02 00 00 00 00 01 5e 6c 01 e0 00
+00 00 01 02 af 56 40 00 00 00 00 00 00 10 00 08 00 00 00 01 00 00 02 ac cf 00 00 00 00 2a f5 3c 60 00 00 00 28 65 93 8a 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0c 00 00 00 00 00 01 19 c0 e0 8c 00 00 00 45 12 76 39 00 00 08 28 2c 11 08 0c
+80 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 80 00 00 00 00 01 02 0c c8 18 10 00 00 0e 5d a7 e0 08 00 00 ab d6 d8 1a 01 08
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 ee e3 98 00 00 80 67 06 44 00 00 00 09 38 ea 10 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 39 80 00 00 00 00 00 00 00 02 ad ce 50 a0 00 04 08 37 f2 06 00 00 20 a3 5e 18 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 03 00 00 80 00 00 00 00 00 00 34 ae e0 0b 00 00 00 19 d7 c0 34 00 00 08 28 0a 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 85 e1 00 08 00 00 0a 73 fd e0 40 00 00 ab d6 d0 0a 80 40
+00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 10 00 00 00 00 00 00 24 c0 9d 8b a0 10 00 99 d2 26 40 00 40 09 38 cb 00 c0 11
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 02 8c 69 18 00 00 00 06 68 06 66 00 00 00 a3 56 00 01 00 80
+00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 39 c2 80 10 18 00 80 96 03 40 40 00 02 08 28 28 00 80 80
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 01 00 15 6b 58 a2 00 04 06 91 81 42 03 00 00 ab d4 c1 99 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 24 c1 a0 10 00 00 00 4b 03 6c 00 00 00 09 38 eb 00 04 08
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 01 02 8d d9 01 c0 00 04 08 73 02 00 00 00 00 a3 5c 11 80 00 40
+00 00 00 00 00 00 00 00 00 80 82 81 a0 00 00 00 00 00 00 00 00 00 06 5e f7 90 00 00 08 f0 00 00 64 00 00 0b 28 4f 32 c0 00
+00 00 00 00 00 00 00 00 00 00 0a bd ce 00 00 01 80 00 00 00 00 00 02 9d dc d1 c0 00 00 0f 01 81 62 84 00 00 b1 4c 08 01 01 58
+00 00 00 00 00 00 00 00 00 20 99 bc d0 00 00 00 00 00 00 00 00 00 3c 01 f0 1e 00 00 00 0f 0f ac 28 00 00 00 65 e9 00 00 00
+00 00 00 00 00 00 00 00 00 00 0a 94 0b 00 00 00 00 00 00 00 00 00 00 3d ed 00 b0 00 00 4a 57 32 00 40 00 00 29 fc 90 18 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3c c3 87 10 a0 00 02 05 02 94 28 00 30 00 00 00 50 00 00
+c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 03 3c fa 00 e4 00 00 2a f5 b7 62 40 03 00 00 00 08 00 00 08
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0c c3 d1 00 00 00 02 25 07 14 40 00 00 0b c0 20 08 01 c0
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 01 a4 59 80 00 00 04 22 e5 fa 02 00 00 00 bc 0c 01 9a 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3c 01 fd 00 00 00 00 0c 07 86 00 00 00 00 ce dd 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3d 59 01 80 00 04 00 e3 7f c0 00 00 00 33 24 e5 80 00 00
+00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 21 c0 e0 00 00 00 00 33 0a 00 28 00 00 01 a7 d0 58 00 14
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 01 e0 dd 01 81 80 04 0c c7 34 06 c0 00 00 5a 45 c9 80 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 43 80 90 00 00 00 f0 16 c0 20 04 00 01 a6 c0 01 00 00
+00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 40 fa 70 a0 00 00 0e 17 70 06 40 20 00 5a 64 a0 1c 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0f 02 e0 00 00 00 02 25 32 86 00 00 30 01 a6 d0 00 04 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 78 5d 01 a0 00 00 22 e7 ef e0 00 02 00 5a 45 80 1a 80 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2b c3 05 80 00 00 02 7f b2 80 2c 00 00 01 a7 d0 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 bc fa f0 00 00 00 63 fd b4 08 00 06 00 5a 64 85 9a 00 18
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 1a 56 80 0b 00 00 00 f0 bb 80 00 00 00 01 a5 c0 50 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f3 cc 19 a0 00 00 46 9d 20 06 00 00 00 5a 41 88 00 01 04
+00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 0c 00 00 00 00 00 00 96 80 c0 00 40 08 00 00 00 00 00 00 30 0b c0 20 00 e0 00
+80 00 00 00 00 00 00 00 03 00 00 00 00 00 80 00 c0 00 00 00 00 00 0a 94 00 02 00 00 80 00 00 00 00 07 02 00 bc 14 00 19 00 0c
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 83 c0 00 00 00 00 00 00 00 04 2c 00 00 10 04 1a 00 e0 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 3c e0 00 00 00 00 00 00 02 06 40 00 10 01 dc d3 80 00 00
+00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 83 c2 00 00 00 00 00 00 00 00 00 00 80 01 4c 2c 01 c0 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 3d 60 01 80 00 00 00 00 00 00 00 00 10 28 dc 10 1d 00 00
+00 02 00 00 00 00 00 00 00 00 00 00 00 00 01 40 00 00 00 07 00 00 00 00 00 00 00 00 00 00 00 04 30 00 00 01 54 18 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 18 00 00 00 00 00 00 00 00 00 00 c0 00 00 00 00 00 00 00 00 00 29 56 81 80 00 00
+00 00 00 00 00 00 02 88 00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 40 00 00 01 4c 2a 00 a4 00
+00 00 20 00 00 00 1c 03 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 18 00 00 00 00 00 00 03 80 00 10 28 dc 00 0a 80 00
+00 00 00 07 7d 6c 20 00 00 00 00 00 00 00 0c 00 00 00 00 00 00 00 00 00 00 00 00 02 12 0a 0b 00 7c 00 00 02 a8 19 00 e0 00
+00 00 00 10 ee 5e 5e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 6a f7 38 07 40 00 00 29 57 95 80 0c 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 2f 00 01 8a 05 07 80 00 80 50 41 05 cd d0 00 00
+00 00 00 00 00 00 14 00 00 00 00 00 00 00 02 01 00 00 00 00 00 00 00 00 00 00 10 01 80 2a f7 30 00 00 04 00 20 57 e1 80 00 18
+00 00 00 00 00 00 00 00 00 a0 c3 c0 00 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 02 8a 0a 02 00 30 00 00 0b c0 00 00 80 00
+00 00 00 00 00 00 00 00 00 00 08 3c 00 00 11 40 40 00 00 00 00 00 00 00 00 00 00 00 00 6a f5 38 00 40 00 00 bc 1e 03 01 00 00
+00 00 00 24 80 74 02 00 02 00 00 00 01 98 00 0c 00 00 00 00 00 40 87 e7 e0 0f 00 0c 02 0a 0b 9c 00 00 20 4a bc 28 19 00 00
+c0 00 00 02 21 16 80 7c 00 20 00 00 00 91 d0 c0 c0 00 00 00 00 00 08 1d 6f 00 e0 00 c8 2a f5 a2 02 00 02 00 81 57 93 9c 00 0c
+00 00 01 24 80 78 00 00 00 21 87 e6 b0 0c 00 00 00 00 00 00 00 00 91 e4 f1 88 00 00 00 aa 0b 40 30 00 00 09 4e cf 00 00 00
+00 00 00 03 21 16 dc 60 80 00 08 1f e9 71 90 00 00 00 00 00 00 04 0b 0e ec b1 90 00 08 0a 55 f0 c0 00 00 80 a8 e6 10 00 00 00
+00 00 00 24 80 34 00 00 00 00 87 c7 b0 00 80 80 00 00 00 00 00 00 93 cd 93 08 00 00 82 aa 03 00 00 00 00 0a 67 4c 10 00 00
+00 00 00 03 21 16 80 00 00 0a 08 1c d8 01 a4 00 00 00 00 00 00 00 18 35 6f 00 80 00 00 2a 57 ec 06 00 00 00 a9 41 98 18 00 00
+00 02 00 24 80 38 20 00 00 04 87 e4 f7 00 00 00 00 00 00 00 00 00 82 fc d0 29 01 00 0a 8d 10 14 00 00 00 08 28 1f 08 00 10
+00 00 00 03 21 17 de 00 00 02 08 1f e8 00 00 00 00 00 00 00 04 00 18 bd 6d 00 c0 08 00 28 d5 e2 c6 00 00 00 ab d6 b7 00 00 80
+00 00 00 64 80 74 40 00 00 00 c3 f7 e0 09 00 00 00 00 00 10 10 00 a2 f5 cf 08 00 40 80 05 0e dc 60 00 80 00 14 3f 00 80 00
+00 00 20 03 21 12 a6 60 20 08 09 1d 5e 10 10 00 00 00 00 00 00 00 1b 0e ee 99 90 00 00 0a f1 2c 67 40 20 00 2b d6 91 81 00 00
+00 00 00 a4 80 78 43 c0 03 04 86 be 97 00 00 00 00 00 00 00 00 00 a8 45 f0 00 00 00 02 47 9b 80 00 00 00 02 a8 29 30 00 00
+00 00 00 03 21 12 e6 04 00 30 0a 9f 4e 18 e0 00 00 00 00 00 00 00 0a 9e ce 00 00 00 01 28 b3 05 e3 80 00 80 29 4d c8 0e 1c 00
+00 00 01 24 80 34 03 80 00 40 8b c5 e7 00 00 14 00 00 00 00 00 00 93 cd e1 0d 00 10 02 47 38 00 7c 00 50 00 14 2f 00 d0 09
+40 00 00 03 21 13 80 00 00 60 08 1d ce 00 00 01 00 00 00 00 00 00 08 35 cf f0 10 01 48 24 77 20 07 00 04 00 2b ce 90 0f 19 10
+00 00 02 24 80 38 00 00 00 20 1d e4 e1 00 00 00 00 00 00 00 80 00 a2 c4 f0 0e 05 40 00 aa 0a c4 24 00 00 22 a8 28 f0 f1 00
+00 00 00 07 21 12 c0 00 00 00 03 b9 7c 80 80 00 00 00 00 00 00 00 08 3d cc 70 00 08 08 0a 57 b2 00 00 00 00 29 4d a0 00 00 00
+00 00
+62 00 3f
+72 00 80
+11 00
+82 00 00
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+82 00 80
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+11 01
+82 00 00
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+82 00 80
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+11 02
+82 00 00
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+82 00 80
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+11 03
+82 00 00
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+82 00 80
+01 03
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00
+22
+84 b1
+01 06
+
diff --git a/fpga/readme.md b/fpga/readme.md new file mode 100644 index 0000000..ad763a1 --- /dev/null +++ b/fpga/readme.md @@ -0,0 +1,72 @@ + / _____) _ | | + ( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | + (______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +FPGA images for LoRa Gateway SX1301AP2-PCB_E336 +=============================================== + +1. Content +---------- + +This directory contains the FPGA images to be programmed in the Semtech's +Reference Design board (SX1301AP2-PCB_E336) flash memory. + +The different images contain the following features: + +* SX1301_FPGA_200K_NOTCH_LBT_SPECTRAL_SCAN_915_v33.hex: + - 200KHz Notch filter for TX (not programmable) + - Listen-Before-Talk for 915+MHz frequency range (Japan) + - Background Spectral Scan (limited) + +* SX1301_FPGA_200K_NOTCH_LBT_SPECTRAL_SCAN_863_v33.hex: + - 200KHz Notch filter for TX (not programmable) + - Listen-Before-Talk for 863+MHz frequency range + - Background Spectral Scan (limited) +Note: This image is the same as the 915+MHz version. It is just meant for +testing "Japan-like" LBT feature on a EU868 board. It does not provide certified +LBT for European band. + +* SX1301_FPGA_NOTCH_PROG_SPECTRAL_SCAN_v31.hex: + - Programmable notch filter for TX + - Background Spectral Scan (full) + +2. Usage +-------- + +The following parameters have to be set when using the Lattice Diamond +Programmer software: + +Device Family -> iCE40 +Device -> iCE40LP1K +Operation -> SPI Flash Programming + -> Programming file: select one of the provided bin image + -> SPI Vendor: Micron + -> SPI Device: SPI-M25P10-A + +3. Legal notice +---------------- + +The information presented in this project documentation does not form part of +any quotation or contract, is believed to be accurate and reliable and may be +changed without notice. No liability will be accepted by the publisher for any +consequence of its use. Publication thereof does not convey nor imply any +license under patent or other industrial or intellectual property rights. +Semtech assumes no responsibility or liability whatsoever for any failure or +unexpected operation resulting from misuse, neglect improper installation, +repair or improper handling or unusual physical or electrical stress +including, but not limited to, exposure to parameters beyond the specified +maximum ratings or operation outside the specified range. + +SEMTECH PRODUCTS ARE NOT DESIGNED, INTENDED, AUTHORIZED OR WARRANTED TO BE +SUITABLE FOR USE IN LIFE-SUPPORT APPLICATIONS, DEVICES OR SYSTEMS OR OTHER +CRITICAL APPLICATIONS. INCLUSION OF SEMTECH PRODUCTS IN SUCH APPLICATIONS IS +UNDERSTOOD TO BE UNDERTAKEN SOLELY AT THE CUSTOMER'S OWN RISK. Should a +customer purchase or use Semtech products for any such unauthorized +application, the customer shall indemnify and hold Semtech and its officers, +employees, subsidiaries, affiliates, and distributors harmless against all +claims, costs damages and attorney fees which could arise. + +*EOF* diff --git a/libloragw/99-libftdi.rules b/libloragw/99-libftdi.rules new file mode 100644 index 0000000..8487413 --- /dev/null +++ b/libloragw/99-libftdi.rules @@ -0,0 +1,8 @@ +# FTDI Devices: FT232BM/L/Q, FT245BM/L/Q, FT232RL/Q, FT245RL/Q, VNC1L with VDPS Firmware +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="0664", GROUP="plugdev" + +# FTDI Devices: FT2232C/D/L, FT2232HL/Q +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", MODE="0664", GROUP="plugdev" + +# FTDI Devices: FT4232HL/Q +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", MODE="0664", GROUP="plugdev" diff --git a/libloragw/Makefile b/libloragw/Makefile new file mode 100644 index 0000000..53c33d9 --- /dev/null +++ b/libloragw/Makefile @@ -0,0 +1,91 @@ +### get external defined data + +LIBLORAGW_VERSION := `cat ../VERSION` +include library.cfg + +### constant symbols + +ARCH ?= +CROSS_COMPILE ?= +CC := $(CROSS_COMPILE)gcc +AR := $(CROSS_COMPILE)ar + +CFLAGS := -O2 -Wall -Wextra -std=c99 -Iinc -I. + +OBJDIR = obj +INCLUDES = $(wildcard inc/*.h) + +### linking options + +LIBS := -lloragw -lrt -lm + +### general build targets + +all: libloragw.a test_loragw_spi test_loragw_reg test_loragw_hal test_loragw_gps test_loragw_cal + +clean: + rm -f libloragw.a + rm -f test_loragw_* + rm -f $(OBJDIR)/*.o + rm -f inc/config.h + +### transpose library.cfg into a C header file : config.h + +inc/config.h: ../VERSION library.cfg + @echo "*** Checking libloragw library configuration ***" + @rm -f $@ + #File initialization + @echo "#ifndef _LORAGW_CONFIGURATION_H" >> $@ + @echo "#define _LORAGW_CONFIGURATION_H" >> $@ + # Release version + @echo "Release version : $(LIBLORAGW_VERSION)" + @echo " #define LIBLORAGW_VERSION "\"$(LIBLORAGW_VERSION)\""" >> $@ + # Debug options + @echo " #define DEBUG_AUX $(DEBUG_AUX)" >> $@ + @echo " #define DEBUG_SPI $(DEBUG_SPI)" >> $@ + @echo " #define DEBUG_REG $(DEBUG_REG)" >> $@ + @echo " #define DEBUG_HAL $(DEBUG_HAL)" >> $@ + @echo " #define DEBUG_GPS $(DEBUG_GPS)" >> $@ + @echo " #define DEBUG_GPIO $(DEBUG_GPIO)" >> $@ + @echo " #define DEBUG_LBT $(DEBUG_LBT)" >> $@ + # end of file + @echo "#endif" >> $@ + @echo "*** Configuration seems ok ***" + +### library module target + +$(OBJDIR): + mkdir -p $(OBJDIR) + +$(OBJDIR)/%.o: src/%.c $(INCLUDES) inc/config.h | $(OBJDIR) + $(CC) -c $(CFLAGS) $< -o $@ + +$(OBJDIR)/loragw_spi.o: src/loragw_spi.native.c $(INCLUDES) inc/config.h | $(OBJDIR) + $(CC) -c $(CFLAGS) $< -o $@ + +$(OBJDIR)/loragw_hal.o: src/loragw_hal.c $(INCLUDES) src/arb_fw.var src/agc_fw.var src/cal_fw.var inc/config.h | $(OBJDIR) + $(CC) -c $(CFLAGS) $< -o $@ + +### static library + +libloragw.a: $(OBJDIR)/loragw_hal.o $(OBJDIR)/loragw_gps.o $(OBJDIR)/loragw_reg.o $(OBJDIR)/loragw_spi.o $(OBJDIR)/loragw_aux.o $(OBJDIR)/loragw_radio.o $(OBJDIR)/loragw_fpga.o $(OBJDIR)/loragw_lbt.o + $(AR) rcs $@ $^ + +### test programs + +test_loragw_spi: tst/test_loragw_spi.c libloragw.a + $(CC) $(CFLAGS) -L. $< -o $@ $(LIBS) + +test_loragw_reg: tst/test_loragw_reg.c libloragw.a + $(CC) $(CFLAGS) -L. $< -o $@ $(LIBS) + +test_loragw_hal: tst/test_loragw_hal.c libloragw.a + $(CC) $(CFLAGS) -L. $< -o $@ $(LIBS) + +test_loragw_gps: tst/test_loragw_gps.c libloragw.a + $(CC) $(CFLAGS) -L. $< -o $@ $(LIBS) + +test_loragw_cal: tst/test_loragw_cal.c libloragw.a src/cal_fw.var + $(CC) $(CFLAGS) -L. $< -o $@ $(LIBS) + +### EOF diff --git a/libloragw/inc/loragw_aux.h b/libloragw/inc/loragw_aux.h new file mode 100644 index 0000000..35b39ab --- /dev/null +++ b/libloragw/inc/loragw_aux.h @@ -0,0 +1,48 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + LoRa concentrator HAL common auxiliary functions + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +#ifndef _LORAGW_AUX_H +#define _LORAGW_AUX_H + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include "config.h" /* library configuration options (dynamically generated) */ + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC MACROS -------------------------------------------------------- */ + +/** +@brief Get a particular bit value from a byte +@param b [in] Any byte from which we want a bit value +@param p [in] Position of the bit in the byte [0..7] +@param n [in] Number of bits we want to get +@return The value corresponding the requested bits +*/ +#define TAKE_N_BITS_FROM(b, p, n) (((b) >> (p)) & ((1 << (n)) - 1)) + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */ + +/** +@brief Wait for a certain time (millisecond accuracy) +@param t number of milliseconds to wait. +*/ +void wait_ms(unsigned long t); + +#endif + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/inc/loragw_fpga.h b/libloragw/inc/loragw_fpga.h new file mode 100644 index 0000000..f599f73 --- /dev/null +++ b/libloragw/inc/loragw_fpga.h @@ -0,0 +1,135 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Functions used to handle FPGA register access for LoRa concentrator. + Registers are addressed by name. + Multi-bytes registers are handled automatically. + Read-modify-write is handled automatically. + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Michael Coracin +*/ + +#ifndef _LORAGW_FPGA_REG_H +#define _LORAGW_FPGA_REG_H + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC CONSTANTS ----------------------------------------------------- */ + +#define LGW_REG_SUCCESS 0 +#define LGW_REG_ERROR -1 + +#define LGW_MIN_NOTCH_FREQ 126000U /* 126 KHz */ +#define LGW_MAX_NOTCH_FREQ 250000U /* 250 KHz */ +#define LGW_DEFAULT_NOTCH_FREQ 129000U /* 129 KHz */ + +/* +auto generated register mapping for C code +this file contains autogenerated C struct used to access the FPGA registers +this file is autogenerated from registers description +*/ + +#define LGW_FPGA_SOFT_RESET 0 +#define LGW_FPGA_FEATURE 1 +#define LGW_FPGA_LBT_INITIAL_FREQ 2 +#define LGW_FPGA_VERSION 3 +#define LGW_FPGA_STATUS 4 +#define LGW_FPGA_CTRL_FEATURE_START 5 +#define LGW_FPGA_CTRL_RADIO_RESET 6 +#define LGW_FPGA_CTRL_INPUT_SYNC_I 7 +#define LGW_FPGA_CTRL_INPUT_SYNC_Q 8 +#define LGW_FPGA_CTRL_OUTPUT_SYNC 9 +#define LGW_FPGA_CTRL_INVERT_IQ 10 +#define LGW_FPGA_CTRL_ACCESS_HISTO_MEM 11 +#define LGW_FPGA_CTRL_CLEAR_HISTO_MEM 12 +#define LGW_FPGA_HISTO_RAM_ADDR 13 +#define LGW_FPGA_HISTO_RAM_DATA 14 +#define LGW_FPGA_HISTO_NB_READ 15 +#define LGW_FPGA_LBT_TIMESTAMP_CH 16 +#define LGW_FPGA_LBT_TIMESTAMP_SELECT_CH 17 +#define LGW_FPGA_LBT_CH0_FREQ_OFFSET 18 +#define LGW_FPGA_LBT_CH1_FREQ_OFFSET 19 +#define LGW_FPGA_LBT_CH2_FREQ_OFFSET 20 +#define LGW_FPGA_LBT_CH3_FREQ_OFFSET 21 +#define LGW_FPGA_LBT_CH4_FREQ_OFFSET 22 +#define LGW_FPGA_LBT_CH5_FREQ_OFFSET 23 +#define LGW_FPGA_LBT_CH6_FREQ_OFFSET 24 +#define LGW_FPGA_LBT_CH7_FREQ_OFFSET 25 +#define LGW_FPGA_SCAN_FREQ_OFFSET 26 +#define LGW_FPGA_LBT_SCAN_TIME_CH0 27 +#define LGW_FPGA_LBT_SCAN_TIME_CH1 28 +#define LGW_FPGA_LBT_SCAN_TIME_CH2 29 +#define LGW_FPGA_LBT_SCAN_TIME_CH3 30 +#define LGW_FPGA_LBT_SCAN_TIME_CH4 31 +#define LGW_FPGA_LBT_SCAN_TIME_CH5 32 +#define LGW_FPGA_LBT_SCAN_TIME_CH6 33 +#define LGW_FPGA_LBT_SCAN_TIME_CH7 34 +#define LGW_FPGA_RSSI_TARGET 35 +#define LGW_FPGA_HISTO_SCAN_FREQ 36 +#define LGW_FPGA_NOTCH_FREQ_OFFSET 37 +#define LGW_FPGA_TOTALREGS 38 + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */ + +/** +@brief LoRa concentrator TX notch filter delay +@return delay in microseconds introduced by TX notch filter +*/ +float lgw_fpga_get_tx_notch_delay(void); + +/** +@brief LoRa concentrator FPGA configuration +@param tx_notch_freq TX notch filter frequency, in Hertz +@return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR) +*/ +int lgw_fpga_configure(uint32_t tx_notch_freq); + +/** +@brief LoRa concentrator FPGA register write +@param register_id register number in the data structure describing registers +@param reg_value signed value to write to the register (for u32, use cast) +@return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR) +*/ +int lgw_fpga_reg_w(uint16_t register_id, int32_t reg_value); + +/** +@brief LoRa concentrator FPGA register read +@param register_id register number in the data structure describing registers +@param reg_value pointer to a variable where to write register read value +@return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR) +*/ +int lgw_fpga_reg_r(uint16_t register_id, int32_t *reg_value); + +/** +@brief LoRa concentrator FPGA register burst write +@param register_id register number in the data structure describing registers +@param data pointer to byte array that will be sent to the LoRa concentrator +@param size size of the transfer, in byte(s) +@return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR) +*/ +int lgw_fpga_reg_wb(uint16_t register_id, uint8_t *data, uint16_t size); + +/** +@brief LoRa concentrator FPGA register burst read +@param register_id register number in the data structure describing registers +@param data pointer to byte array that will be written from the LoRa concentrator +@param size size of the transfer, in byte(s) +@return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR) +*/ +int lgw_fpga_reg_rb(uint16_t register_id, uint8_t *data, uint16_t size); + +#endif +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/inc/loragw_gps.h b/libloragw/inc/loragw_gps.h new file mode 100644 index 0000000..6dbd30b --- /dev/null +++ b/libloragw/inc/loragw_gps.h @@ -0,0 +1,235 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Library of functions to manage a GNSS module (typically GPS) for accurate + timestamping of packets and synchronisation of gateways. + A limited set of module brands/models are supported. + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Michael Coracin +*/ + + +#ifndef _LORAGW_GPS_H +#define _LORAGW_GPS_H + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#define _GNU_SOURCE +#include <stdint.h> /* C99 types */ +#include <time.h> /* time library */ +#include <termios.h> /* speed_t */ +#include <unistd.h> /* ssize_t */ + +#include "config.h" /* library configuration options (dynamically generated) */ + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC TYPES --------------------------------------------------------- */ + +/** +@struct coord_s +@brief Time solution required for timestamp to absolute time conversion +*/ +struct tref { + time_t systime; /*!> system time when solution was calculated */ + uint32_t count_us; /*!> reference concentrator internal timestamp */ + struct timespec utc; /*!> reference UTC time (from GPS/NMEA) */ + struct timespec gps; /*!> reference GPS time (since 01.Jan.1980) */ + double xtal_err; /*!> raw clock error (eg. <1 'slow' XTAL) */ +}; + +/** +@struct coord_s +@brief Geodesic coordinates +*/ +struct coord_s { + double lat; /*!> latitude [-90,90] (North +, South -) */ + double lon; /*!> longitude [-180,180] (East +, West -)*/ + short alt; /*!> altitude in meters (WGS 84 geoid ref.) */ +}; + +/** +@enum gps_msg +@brief Type of GPS (and other GNSS) sentences +*/ +enum gps_msg { + UNKNOWN, /*!> neutral value */ + IGNORED, /*!> frame was not parsed by the system */ + INVALID, /*!> system try to parse frame but failed */ + INCOMPLETE, /*!> frame parsed was missing bytes */ + /* NMEA messages of interest */ + NMEA_RMC, /*!> Recommended Minimum data (time + date) */ + NMEA_GGA, /*!> Global positioning system fix data (pos + alt) */ + NMEA_GNS, /*!> GNSS fix data (pos + alt, sat number) */ + NMEA_ZDA, /*!> Time and Date */ + /* NMEA message useful for time reference quality assessment */ + NMEA_GBS, /*!> GNSS Satellite Fault Detection */ + NMEA_GST, /*!> GNSS Pseudo Range Error Statistics */ + NMEA_GSA, /*!> GNSS DOP and Active Satellites (sat number) */ + NMEA_GSV, /*!> GNSS Satellites in View (sat SNR) */ + /* Misc. NMEA messages */ + NMEA_GLL, /*!> Latitude and longitude, with time fix and status */ + NMEA_TXT, /*!> Text Transmission */ + NMEA_VTG, /*!> Course over ground and Ground speed */ + /* uBlox proprietary NMEA messages of interest */ + UBX_NAV_TIMEGPS, /*!> GPS Time Solution */ + UBX_NAV_TIMEUTC /*!> UTC Time Solution */ +}; + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC CONSTANTS ----------------------------------------------------- */ + +#define LGW_GPS_SUCCESS 0 +#define LGW_GPS_ERROR -1 + +#define LGW_GPS_MIN_MSG_SIZE (8) +#define LGW_GPS_UBX_SYNC_CHAR (0xB5) +#define LGW_GPS_NMEA_SYNC_CHAR (0x24) + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */ + +/** +@brief Configure a GPS module + +@param tty_path path to the TTY connected to the GPS +@param gps_familly parameter (eg. ubx6 for uBlox gen.6) +@param target_brate target baudrate for communication (0 keeps default target baudrate) +@param fd_ptr pointer to a variable to receive file descriptor on GPS tty +@return success if the function was able to connect and configure a GPS module +*/ +int lgw_gps_enable(char* tty_path, char* gps_familly, speed_t target_brate, int* fd_ptr); + +/** +@brief Restore GPS serial configuration and close serial device + +@param fd file descriptor on GPS tty +@return success if the function was able to complete +*/ +int lgw_gps_disable(int fd); + +/** +@brief Parse messages coming from the GPS system (or other GNSS) + +@param serial_buff pointer to the string to be parsed +@param buff_size maximum string lengths for NMEA parsing (incl. null char) +@return type of frame parsed + +The RAW NMEA sentences are parsed to a global set of variables shared with the +lgw_gps_get function. +If the lgw_parse_nmea and lgw_gps_get are used in different threads, a mutex +lock must be acquired before calling either function. +*/ +enum gps_msg lgw_parse_nmea(const char* serial_buff, int buff_size); + +/** +@brief Parse Ublox proprietary messages coming from the GPS system + +@param serial_buff pointer to the string to be parsed +@param buff_size maximum string lengths for UBX parsing (incl. null char) +@param msg_size number of bytes parsed as UBX message if found +@return type of frame parsed + +The RAW UBX sentences are parsed to a global set of variables shared with the +lgw_gps_get function. +If the lgw_parse_ubx and lgw_gps_get are used in different threads, a mutex +lock must be acquired before calling either function. +*/ +enum gps_msg lgw_parse_ubx(const char* serial_buff, size_t buff_size, size_t *msg_size); + +/** +@brief Get the GPS solution (space & time) for the concentrator + +@param utc pointer to store UTC time, with ns precision (NULL to ignore) +@param gps_time pointer to store GPS time, with ns precision (NULL to ignore) +@param loc pointer to store coordinates (NULL to ignore) +@param err pointer to store coordinates standard deviation (NULL to ignore) +@return success if the chosen elements could be returned + +This function read the global variables generated by the NMEA/UBX parsing +functions lgw_parse_nmea/lgw_parse_ubx. It returns time and location data in a +format that is exploitable by other functions in that library sub-module. +If the lgw_parse_nmea/lgw_parse_ubx and lgw_gps_get are used in different +threads, a mutex lock must be acquired before calling either function. +*/ +int lgw_gps_get(struct timespec *utc, struct timespec *gps_time, struct coord_s *loc, struct coord_s *err); + +/** +@brief Get time and position information from the serial GPS last message received +@param utc UTC time, with ns precision (leap seconds are ignored) +@param gps_time timestamp of last time pulse from the GPS module converted to the UNIX epoch + (leap seconds are ignored) +@param loc location information +@param err location error estimate if supported +@return success if timestamp was read and time reference could be refreshed + +Set systime to 0 in ref to trigger initial synchronization. +*/ +int lgw_gps_sync(struct tref *ref, uint32_t count_us, struct timespec utc, struct timespec gps_time); + +/** +@brief Convert concentrator timestamp counter value to UTC time + +@param ref time reference structure required for time conversion +@param count_us internal timestamp counter of the LoRa concentrator +@param utc pointer to store UTC time, with ns precision (leap seconds ignored) +@return success if the function was able to convert timestamp to UTC + +This function is typically used when a packet is received to transform the +internal counter-based timestamp in an absolute timestamp with an accuracy in +the order of a couple microseconds (ns resolution). +*/ +int lgw_cnt2utc(struct tref ref, uint32_t count_us, struct timespec* utc); + +/** +@brief Convert UTC time to concentrator timestamp counter value + +@param ref time reference structure required for time conversion +@param utc UTC time, with ns precision (leap seconds are ignored) +@param count_us pointer to store internal timestamp counter of LoRa concentrator +@return success if the function was able to convert UTC to timestamp + +This function is typically used when a packet must be sent at an accurate time +(eg. to send a piggy-back response after receiving a packet from a node) to +transform an absolute UTC time into a matching internal concentrator timestamp. +*/ +int lgw_utc2cnt(struct tref ref,struct timespec utc, uint32_t* count_us); + +/** +@brief Convert concentrator timestamp counter value to GPS time + +@param ref time reference structure required for time conversion +@param count_us internal timestamp counter of the LoRa concentrator +@param gps_time pointer to store GPS time, with ns precision (leap seconds ignored) +@return success if the function was able to convert timestamp to GPS time + +This function is typically used when a packet is received to transform the +internal counter-based timestamp in an absolute timestamp with an accuracy in +the order of a millisecond. +*/ +int lgw_cnt2gps(struct tref ref, uint32_t count_us, struct timespec* gps_time); + +/** +@brief Convert GPS time to concentrator timestamp counter value + +@param ref time reference structure required for time conversion +@param gps_time GPS time, with ns precision (leap seconds are ignored) +@param count_us pointer to store internal timestamp counter of LoRa concentrator +@return success if the function was able to convert GPS time to timestamp + +This function is typically used when a packet must be sent at an accurate time +(eg. to send a piggy-back response after receiving a packet from a node) to +transform an absolute GPS time into a matching internal concentrator timestamp. +*/ +int lgw_gps2cnt(struct tref ref, struct timespec gps_time, uint32_t* count_us); + +#endif + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/inc/loragw_hal.h b/libloragw/inc/loragw_hal.h new file mode 100644 index 0000000..d5f9ade --- /dev/null +++ b/libloragw/inc/loragw_hal.h @@ -0,0 +1,419 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + LoRa concentrator Hardware Abstraction Layer + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +#ifndef _LORAGW_HAL_H +#define _LORAGW_HAL_H + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ + +#include "config.h" /* library configuration options (dynamically generated) */ + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC MACROS -------------------------------------------------------- */ + +#define IS_LORA_BW(bw) ((bw == BW_125KHZ) || (bw == BW_250KHZ) || (bw == BW_500KHZ)) +#define IS_LORA_STD_DR(dr) ((dr == DR_LORA_SF7) || (dr == DR_LORA_SF8) || (dr == DR_LORA_SF9) || (dr == DR_LORA_SF10) || (dr == DR_LORA_SF11) || (dr == DR_LORA_SF12)) +#define IS_LORA_MULTI_DR(dr) ((dr & ~DR_LORA_MULTI) == 0) /* ones outside of DR_LORA_MULTI bitmask -> not a combination of LoRa datarates */ +#define IS_LORA_CR(cr) ((cr == CR_LORA_4_5) || (cr == CR_LORA_4_6) || (cr == CR_LORA_4_7) || (cr == CR_LORA_4_8)) + +#define IS_FSK_BW(bw) ((bw >= 1) && (bw <= 7)) +#define IS_FSK_DR(dr) ((dr >= DR_FSK_MIN) && (dr <= DR_FSK_MAX)) + +#define IS_TX_MODE(mode) ((mode == IMMEDIATE) || (mode == TIMESTAMPED) || (mode == ON_GPS)) + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC CONSTANTS ----------------------------------------------------- */ + +/* return status code */ +#define LGW_HAL_SUCCESS 0 +#define LGW_HAL_ERROR -1 +#define LGW_LBT_ISSUE 1 + +/* radio-specific parameters */ +#define LGW_XTAL_FREQU 32000000 /* frequency of the RF reference oscillator */ +#define LGW_RF_CHAIN_NB 2 /* number of RF chains */ +#define LGW_RF_RX_BANDWIDTH {1000000, 1000000} /* bandwidth of the radios */ + +/* type of if_chain + modem */ +#define IF_UNDEFINED 0 +#define IF_LORA_STD 0x10 /* if + standard single-SF LoRa modem */ +#define IF_LORA_MULTI 0x11 /* if + LoRa receiver with multi-SF capability */ +#define IF_FSK_STD 0x20 /* if + standard FSK modem */ + +/* concentrator chipset-specific parameters */ +/* to use array parameters, declare a local const and use 'if_chain' as index */ +#define LGW_IF_CHAIN_NB 10 /* number of IF+modem RX chains */ +#define LGW_PKT_FIFO_SIZE 16 /* depth of the RX packet FIFO */ +#define LGW_DATABUFF_SIZE 1024 /* size in bytes of the RX data buffer (contains payload & metadata) */ +#define LGW_REF_BW 125000 /* typical bandwidth of data channel */ +#define LGW_MULTI_NB 8 /* number of LoRa 'multi SF' chains */ +#define LGW_IFMODEM_CONFIG {\ + IF_LORA_MULTI, \ + IF_LORA_MULTI, \ + IF_LORA_MULTI, \ + IF_LORA_MULTI, \ + IF_LORA_MULTI, \ + IF_LORA_MULTI, \ + IF_LORA_MULTI, \ + IF_LORA_MULTI, \ + IF_LORA_STD, \ + IF_FSK_STD } /* configuration of available IF chains and modems on the hardware */ + +/* values available for the 'modulation' parameters */ +/* NOTE: arbitrary values */ +#define MOD_UNDEFINED 0 +#define MOD_LORA 0x10 +#define MOD_FSK 0x20 + +/* values available for the 'bandwidth' parameters (LoRa & FSK) */ +/* NOTE: directly encode FSK RX bandwidth, do not change */ +#define BW_UNDEFINED 0 +#define BW_500KHZ 0x01 +#define BW_250KHZ 0x02 +#define BW_125KHZ 0x03 +#define BW_62K5HZ 0x04 +#define BW_31K2HZ 0x05 +#define BW_15K6HZ 0x06 +#define BW_7K8HZ 0x07 + +/* values available for the 'datarate' parameters */ +/* NOTE: LoRa values used directly to code SF bitmask in 'multi' modem, do not change */ +#define DR_UNDEFINED 0 +#define DR_LORA_SF7 0x02 +#define DR_LORA_SF8 0x04 +#define DR_LORA_SF9 0x08 +#define DR_LORA_SF10 0x10 +#define DR_LORA_SF11 0x20 +#define DR_LORA_SF12 0x40 +#define DR_LORA_MULTI 0x7E +/* NOTE: for FSK directly use baudrate between 500 bauds and 250 kbauds */ +#define DR_FSK_MIN 500 +#define DR_FSK_MAX 250000 + +/* values available for the 'coderate' parameters (LoRa only) */ +/* NOTE: arbitrary values */ +#define CR_UNDEFINED 0 +#define CR_LORA_4_5 0x01 +#define CR_LORA_4_6 0x02 +#define CR_LORA_4_7 0x03 +#define CR_LORA_4_8 0x04 + +/* values available for the 'status' parameter */ +/* NOTE: values according to hardware specification */ +#define STAT_UNDEFINED 0x00 +#define STAT_NO_CRC 0x01 +#define STAT_CRC_BAD 0x11 +#define STAT_CRC_OK 0x10 + +/* values available for the 'tx_mode' parameter */ +#define IMMEDIATE 0 +#define TIMESTAMPED 1 +#define ON_GPS 2 +//#define ON_EVENT 3 +//#define GPS_DELAYED 4 +//#define EVENT_DELAYED 5 + +/* values available for 'select' in the status function */ +#define TX_STATUS 1 +#define RX_STATUS 2 + +/* status code for TX_STATUS */ +/* NOTE: arbitrary values */ +#define TX_STATUS_UNKNOWN 0 +#define TX_OFF 1 /* TX modem disabled, it will ignore commands */ +#define TX_FREE 2 /* TX modem is free, ready to receive a command */ +#define TX_SCHEDULED 3 /* TX modem is loaded, ready to send the packet after an event and/or delay */ +#define TX_EMITTING 4 /* TX modem is emitting */ + +/* status code for RX_STATUS */ +/* NOTE: arbitrary values */ +#define RX_STATUS_UNKNOWN 0 +#define RX_OFF 1 /* RX modem is disabled, it will ignore commands */ +#define RX_ON 2 /* RX modem is receiving */ +#define RX_SUSPENDED 3 /* RX is suspended while a TX is ongoing */ + +/* Maximum size of Tx gain LUT */ +#define TX_GAIN_LUT_SIZE_MAX 16 + +/* LBT constants */ +#define LBT_CHANNEL_FREQ_NB 8 /* Number of LBT channels */ + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC TYPES --------------------------------------------------------- */ + +/** +@enum lgw_radio_type_e +@brief Radio types that can be found on the LoRa Gateway +*/ +enum lgw_radio_type_e { + LGW_RADIO_TYPE_NONE, + LGW_RADIO_TYPE_SX1255, + LGW_RADIO_TYPE_SX1257, + LGW_RADIO_TYPE_SX1272, + LGW_RADIO_TYPE_SX1276 +}; + +/** +@struct lgw_conf_board_s +@brief Configuration structure for board specificities +*/ +struct lgw_conf_board_s { + bool lorawan_public; /*!> Enable ONLY for *public* networks using the LoRa MAC protocol */ + uint8_t clksrc; /*!> Index of RF chain which provides clock to concentrator */ +}; + +/** +@struct lgw_conf_lbt_chan_s +@brief Configuration structure for LBT channels +*/ +struct lgw_conf_lbt_chan_s { + uint32_t freq_hz; + uint16_t scan_time_us; +}; + +/** +@struct lgw_conf_lbt_s +@brief Configuration structure for LBT specificities +*/ +struct lgw_conf_lbt_s { + bool enable; /*!> enable or disable LBT */ + int8_t rssi_target; /*!> RSSI threshold to detect if channel is busy or not (dBm) */ + uint8_t nb_channel; /*!> number of LBT channels */ + struct lgw_conf_lbt_chan_s channels[LBT_CHANNEL_FREQ_NB]; + int8_t rssi_offset; /*!> RSSI offset to be applied to SX127x RSSI values */ +}; + +/** +@struct lgw_conf_rxrf_s +@brief Configuration structure for a RF chain +*/ +struct lgw_conf_rxrf_s { + bool enable; /*!> enable or disable that RF chain */ + uint32_t freq_hz; /*!> center frequency of the radio in Hz */ + float rssi_offset; /*!> Board-specific RSSI correction factor */ + enum lgw_radio_type_e type; /*!> Radio type for that RF chain (SX1255, SX1257....) */ + bool tx_enable; /*!> enable or disable TX on that RF chain */ + uint32_t tx_notch_freq; /*!> TX notch filter frequency [126KHz..250KHz] */ +}; + +/** +@struct lgw_conf_rxif_s +@brief Configuration structure for an IF chain +*/ +struct lgw_conf_rxif_s { + bool enable; /*!> enable or disable that IF chain */ + uint8_t rf_chain; /*!> to which RF chain is that IF chain associated */ + int32_t freq_hz; /*!> center frequ of the IF chain, relative to RF chain frequency */ + uint8_t bandwidth; /*!> RX bandwidth, 0 for default */ + uint32_t datarate; /*!> RX datarate, 0 for default */ + uint8_t sync_word_size; /*!> size of FSK sync word (number of bytes, 0 for default) */ + uint64_t sync_word; /*!> FSK sync word (ALIGN RIGHT, eg. 0xC194C1) */ +}; + +/** +@struct lgw_pkt_rx_s +@brief Structure containing the metadata of a packet that was received and a pointer to the payload +*/ +struct lgw_pkt_rx_s { + uint32_t freq_hz; /*!> central frequency of the IF chain */ + uint8_t if_chain; /*!> by which IF chain was packet received */ + uint8_t status; /*!> status of the received packet */ + uint32_t count_us; /*!> internal concentrator counter for timestamping, 1 microsecond resolution */ + uint8_t rf_chain; /*!> through which RF chain the packet was received */ + uint8_t modulation; /*!> modulation used by the packet */ + uint8_t bandwidth; /*!> modulation bandwidth (LoRa only) */ + uint32_t datarate; /*!> RX datarate of the packet (SF for LoRa) */ + uint8_t coderate; /*!> error-correcting code of the packet (LoRa only) */ + float rssi; /*!> average packet RSSI in dB */ + float snr; /*!> average packet SNR, in dB (LoRa only) */ + float snr_min; /*!> minimum packet SNR, in dB (LoRa only) */ + float snr_max; /*!> maximum packet SNR, in dB (LoRa only) */ + uint16_t crc; /*!> CRC that was received in the payload */ + uint16_t size; /*!> payload size in bytes */ + uint8_t payload[256]; /*!> buffer containing the payload */ +}; + +/** +@struct lgw_pkt_tx_s +@brief Structure containing the configuration of a packet to send and a pointer to the payload +*/ +struct lgw_pkt_tx_s { + uint32_t freq_hz; /*!> center frequency of TX */ + uint8_t tx_mode; /*!> select on what event/time the TX is triggered */ + uint32_t count_us; /*!> timestamp or delay in microseconds for TX trigger */ + uint8_t rf_chain; /*!> through which RF chain will the packet be sent */ + int8_t rf_power; /*!> TX power, in dBm */ + uint8_t modulation; /*!> modulation to use for the packet */ + uint8_t bandwidth; /*!> modulation bandwidth (LoRa only) */ + uint32_t datarate; /*!> TX datarate (baudrate for FSK, SF for LoRa) */ + uint8_t coderate; /*!> error-correcting code of the packet (LoRa only) */ + bool invert_pol; /*!> invert signal polarity, for orthogonal downlinks (LoRa only) */ + uint8_t f_dev; /*!> frequency deviation, in kHz (FSK only) */ + uint16_t preamble; /*!> set the preamble length, 0 for default */ + bool no_crc; /*!> if true, do not send a CRC in the packet */ + bool no_header; /*!> if true, enable implicit header mode (LoRa), fixed length (FSK) */ + uint16_t size; /*!> payload size in bytes */ + uint8_t payload[256]; /*!> buffer containing the payload */ +}; + +/** +@struct lgw_tx_gain_s +@brief Structure containing all gains of Tx chain +*/ +struct lgw_tx_gain_s { + uint8_t dig_gain; /*!> 2 bits, control of the digital gain of SX1301 */ + uint8_t pa_gain; /*!> 2 bits, control of the external PA (SX1301 I/O) */ + uint8_t dac_gain; /*!> 2 bits, control of the radio DAC */ + uint8_t mix_gain; /*!> 4 bits, control of the radio mixer */ + int8_t rf_power; /*!> measured TX power at the board connector, in dBm */ +}; + +/** +@struct lgw_tx_gain_lut_s +@brief Structure defining the Tx gain LUT +*/ +struct lgw_tx_gain_lut_s { + struct lgw_tx_gain_s lut[TX_GAIN_LUT_SIZE_MAX]; /*!> Array of Tx gain struct */ + uint8_t size; /*!> Number of LUT indexes */ +}; + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */ + +/** +@brief Configure the gateway board +@param conf structure containing the configuration parameters +@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else +*/ +int lgw_board_setconf(struct lgw_conf_board_s conf); + +/** +@brief Configure the gateway lbt function +@param conf structure containing the configuration parameters +@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else +*/ +int lgw_lbt_setconf(struct lgw_conf_lbt_s conf); + +/** +@brief Configure an RF chain (must configure before start) +@param rf_chain number of the RF chain to configure [0, LGW_RF_CHAIN_NB - 1] +@param conf structure containing the configuration parameters +@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else +*/ +int lgw_rxrf_setconf(uint8_t rf_chain, struct lgw_conf_rxrf_s conf); + +/** +@brief Configure an IF chain + modem (must configure before start) +@param if_chain number of the IF chain + modem to configure [0, LGW_IF_CHAIN_NB - 1] +@param conf structure containing the configuration parameters +@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else +*/ +int lgw_rxif_setconf(uint8_t if_chain, struct lgw_conf_rxif_s conf); + +/** +@brief Configure the Tx gain LUT +@param pointer to structure defining the LUT +@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else +*/ +int lgw_txgain_setconf(struct lgw_tx_gain_lut_s *conf); + +/** +@brief Connect to the LoRa concentrator, reset it and configure it according to previously set parameters +@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else +*/ +int lgw_start(void); + +/** +@brief Stop the LoRa concentrator and disconnect it +@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else +*/ +int lgw_stop(void); + +/** +@brief A non-blocking function that will fetch up to 'max_pkt' packets from the LoRa concentrator FIFO and data buffer +@param max_pkt maximum number of packet that must be retrieved (equal to the size of the array of struct) +@param pkt_data pointer to an array of struct that will receive the packet metadata and payload pointers +@return LGW_HAL_ERROR id the operation failed, else the number of packets retrieved +*/ +int lgw_receive(uint8_t max_pkt, struct lgw_pkt_rx_s *pkt_data); + +/** +@brief Schedule a packet to be send immediately or after a delay depending on tx_mode +@param pkt_data structure containing the data and metadata for the packet to send +@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else + +/!\ When sending a packet, there is a delay (approx 1.5ms) for the analog +circuitry to start and be stable. This delay is adjusted by the HAL depending +on the board version (lgw_i_tx_start_delay_us). + +In 'timestamp' mode, this is transparent: the modem is started +lgw_i_tx_start_delay_us microseconds before the user-set timestamp value is +reached, the preamble of the packet start right when the internal timestamp +counter reach target value. + +In 'immediate' mode, the packet is emitted as soon as possible: transferring the +packet (and its parameters) from the host to the concentrator takes some time, +then there is the lgw_i_tx_start_delay_us, then the packet is emitted. + +In 'triggered' mode (aka PPS/GPS mode), the packet, typically a beacon, is +emitted lgw_i_tx_start_delay_us microsenconds after a rising edge of the +trigger signal. Because there is no way to anticipate the triggering event and +start the analog circuitry beforehand, that delay must be taken into account in +the protocol. +*/ +int lgw_send(struct lgw_pkt_tx_s pkt_data); + +/** +@brief Give the the status of different part of the LoRa concentrator +@param select is used to select what status we want to know +@param code is used to return the status code +@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else +*/ +int lgw_status(uint8_t select, uint8_t *code); + +/** +@brief Abort a currently scheduled or ongoing TX +@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else +*/ +int lgw_abort_tx(void); + +/** +@brief Return value of internal counter when latest event (eg GPS pulse) was captured +@param trig_cnt_us pointer to receive timestamp value +@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else +*/ +int lgw_get_trigcnt(uint32_t* trig_cnt_us); + +/** +@brief Allow user to check the version/options of the library once compiled +@return pointer on a human-readable null terminated string +*/ +const char* lgw_version_info(void); + +/** +@brief Return time on air of given packet, in milliseconds +@param packet is a pointer to the packet structure +@return the packet time on air in milliseconds +*/ +uint32_t lgw_time_on_air(struct lgw_pkt_tx_s *packet); + +#endif + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/inc/loragw_lbt.h b/libloragw/inc/loragw_lbt.h new file mode 100644 index 0000000..cb8aada --- /dev/null +++ b/libloragw/inc/loragw_lbt.h @@ -0,0 +1,70 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Functions used to handle the Listen Before Talk feature + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Michael Coracin +*/ + +#ifndef _LORAGW_LBT_H +#define _LORAGW_LBT_H + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ + +#include "loragw_hal.h" + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC CONSTANTS ----------------------------------------------------- */ + +#define LGW_LBT_SUCCESS 0 +#define LGW_LBT_ERROR -1 + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */ + +/** +@brief Set the configuration parameters for LBT feature +@param conf structure containing the configuration parameters +@return LGW_LBT_ERROR id the operation failed, LGW_LBT_SUCCESS else +*/ +int lbt_setconf(struct lgw_conf_lbt_s * conf); + +/** +@brief Configure the concentrator for LBT feature +@return LGW_LBT_ERROR id the operation failed, LGW_LBT_SUCCESS else +*/ +int lbt_setup(void); + +/** +@brief Start the LBT FSM +@return LGW_LBT_ERROR id the operation failed, LGW_LBT_SUCCESS else +*/ +int lbt_start(void); + +/** +@brief Configure the concentrator for LBT feature +@param pkt_data pointer to downlink packet to be trabsmitted +@param tx_allowed pointer to receive permission for transmission +@return LGW_LBT_ERROR id the operation failed, LGW_LBT_SUCCESS else +*/ +int lbt_is_channel_free(struct lgw_pkt_tx_s * pkt_data, uint16_t tx_start_delay, bool * tx_allowed); + +/** +@brief Check if LBT is enabled +@return true if enabled, false otherwise +*/ +bool lbt_is_enabled(void); + +#endif +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/inc/loragw_radio.h b/libloragw/inc/loragw_radio.h new file mode 100644 index 0000000..32d494b --- /dev/null +++ b/libloragw/inc/loragw_radio.h @@ -0,0 +1,73 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Functions used to handle LoRa concentrator radios. + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Michael Coracin +*/ + +#ifndef _LORAGW_RADIO_H +#define _LORAGW_RADIO_H + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC CONSTANTS ----------------------------------------------------- */ + +#define LGW_REG_SUCCESS 0 +#define LGW_REG_ERROR -1 + +#define SX125x_32MHz_FRAC 15625 /* irreductible fraction for PLL register caculation */ + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC CONSTANTS ----------------------------------------------------- */ + +enum lgw_sx127x_rxbw_e { + LGW_SX127X_RXBW_2K6_HZ, + LGW_SX127X_RXBW_3K1_HZ, + LGW_SX127X_RXBW_3K9_HZ, + LGW_SX127X_RXBW_5K2_HZ, + LGW_SX127X_RXBW_6K3_HZ, + LGW_SX127X_RXBW_7K8_HZ, + LGW_SX127X_RXBW_10K4_HZ, + LGW_SX127X_RXBW_12K5_HZ, + LGW_SX127X_RXBW_15K6_HZ, + LGW_SX127X_RXBW_20K8_HZ, + LGW_SX127X_RXBW_25K_HZ, + LGW_SX127X_RXBW_31K3_HZ, + LGW_SX127X_RXBW_41K7_HZ, + LGW_SX127X_RXBW_50K_HZ, + LGW_SX127X_RXBW_62K5_HZ, + LGW_SX127X_RXBW_83K3_HZ, + LGW_SX127X_RXBW_100K_HZ, + LGW_SX127X_RXBW_125K_HZ, + LGW_SX127X_RXBW_166K7_HZ, + LGW_SX127X_RXBW_200K_HZ, + LGW_SX127X_RXBW_250K_HZ +}; + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */ + +int lgw_setup_sx125x(uint8_t rf_chain, uint8_t rf_clkout, bool rf_enable, uint8_t rf_radio_type, uint32_t freq_hz); + +int lgw_setup_sx127x(uint32_t frequency, uint8_t modulation, enum lgw_sx127x_rxbw_e rxbw_khz, int8_t rssi_offset); + +int lgw_sx127x_reg_w(uint8_t address, uint8_t reg_value); + +int lgw_sx127x_reg_r(uint8_t address, uint8_t *reg_value); + + +#endif +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/inc/loragw_reg.h b/libloragw/inc/loragw_reg.h new file mode 100644 index 0000000..2a944a7 --- /dev/null +++ b/libloragw/inc/loragw_reg.h @@ -0,0 +1,461 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Functions used to handle a single LoRa concentrator. + Registers are addressed by name. + Multi-bytes registers are handled automatically. + Read-modify-write is handled automatically. + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +#ifndef _LORAGW_REG_H +#define _LORAGW_REG_H + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ + +#include "config.h" /* library configuration options (dynamically generated) */ + +/* -------------------------------------------------------------------------- */ +/* --- INTERNAL SHARED TYPES ------------------------------------------------ */ + +struct lgw_reg_s { + int8_t page; /*!< page containing the register (-1 for all pages) */ + uint8_t addr; /*!< base address of the register (7 bit) */ + uint8_t offs; /*!< position of the register LSB (between 0 to 7) */ + bool sign; /*!< 1 indicates the register is signed (2 complem.) */ + uint8_t leng; /*!< number of bits in the register */ + bool rdon; /*!< 1 indicates a read-only register */ + int32_t dflt; /*!< register default value */ +}; + +/* -------------------------------------------------------------------------- */ +/* --- INTERNAL SHARED FUNCTIONS -------------------------------------------- */ + +int reg_w_align32(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, struct lgw_reg_s r, int32_t reg_value); +int reg_r_align32(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, struct lgw_reg_s r, int32_t *reg_value); + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC CONSTANTS ----------------------------------------------------- */ + +#define LGW_REG_SUCCESS 0 +#define LGW_REG_ERROR -1 + +/* +auto generated register mapping for C code : 11-Jul-2013 13:20:40 +this file contains autogenerated C struct used to access the LORA registers +this file is autogenerated from registers description +293 registers are defined +*/ + +#define LGW_PAGE_REG 0 +#define LGW_SOFT_RESET 1 +#define LGW_VERSION 2 +#define LGW_RX_DATA_BUF_ADDR 3 +#define LGW_RX_DATA_BUF_DATA 4 +#define LGW_TX_DATA_BUF_ADDR 5 +#define LGW_TX_DATA_BUF_DATA 6 +#define LGW_CAPTURE_RAM_ADDR 7 +#define LGW_CAPTURE_RAM_DATA 8 +#define LGW_MCU_PROM_ADDR 9 +#define LGW_MCU_PROM_DATA 10 +#define LGW_RX_PACKET_DATA_FIFO_NUM_STORED 11 +#define LGW_RX_PACKET_DATA_FIFO_ADDR_POINTER 12 +#define LGW_RX_PACKET_DATA_FIFO_STATUS 13 +#define LGW_RX_PACKET_DATA_FIFO_PAYLOAD_SIZE 14 +#define LGW_MBWSSF_MODEM_ENABLE 15 +#define LGW_CONCENTRATOR_MODEM_ENABLE 16 +#define LGW_FSK_MODEM_ENABLE 17 +#define LGW_GLOBAL_EN 18 +#define LGW_CLK32M_EN 19 +#define LGW_CLKHS_EN 20 +#define LGW_START_BIST0 21 +#define LGW_START_BIST1 22 +#define LGW_CLEAR_BIST0 23 +#define LGW_CLEAR_BIST1 24 +#define LGW_BIST0_FINISHED 25 +#define LGW_BIST1_FINISHED 26 +#define LGW_MCU_AGC_PROG_RAM_BIST_STATUS 27 +#define LGW_MCU_ARB_PROG_RAM_BIST_STATUS 28 +#define LGW_CAPTURE_RAM_BIST_STATUS 29 +#define LGW_CHAN_FIR_RAM0_BIST_STATUS 30 +#define LGW_CHAN_FIR_RAM1_BIST_STATUS 31 +#define LGW_CORR0_RAM_BIST_STATUS 32 +#define LGW_CORR1_RAM_BIST_STATUS 33 +#define LGW_CORR2_RAM_BIST_STATUS 34 +#define LGW_CORR3_RAM_BIST_STATUS 35 +#define LGW_CORR4_RAM_BIST_STATUS 36 +#define LGW_CORR5_RAM_BIST_STATUS 37 +#define LGW_CORR6_RAM_BIST_STATUS 38 +#define LGW_CORR7_RAM_BIST_STATUS 39 +#define LGW_MODEM0_RAM0_BIST_STATUS 40 +#define LGW_MODEM1_RAM0_BIST_STATUS 41 +#define LGW_MODEM2_RAM0_BIST_STATUS 42 +#define LGW_MODEM3_RAM0_BIST_STATUS 43 +#define LGW_MODEM4_RAM0_BIST_STATUS 44 +#define LGW_MODEM5_RAM0_BIST_STATUS 45 +#define LGW_MODEM6_RAM0_BIST_STATUS 46 +#define LGW_MODEM7_RAM0_BIST_STATUS 47 +#define LGW_MODEM0_RAM1_BIST_STATUS 48 +#define LGW_MODEM1_RAM1_BIST_STATUS 49 +#define LGW_MODEM2_RAM1_BIST_STATUS 50 +#define LGW_MODEM3_RAM1_BIST_STATUS 51 +#define LGW_MODEM4_RAM1_BIST_STATUS 52 +#define LGW_MODEM5_RAM1_BIST_STATUS 53 +#define LGW_MODEM6_RAM1_BIST_STATUS 54 +#define LGW_MODEM7_RAM1_BIST_STATUS 55 +#define LGW_MODEM0_RAM2_BIST_STATUS 56 +#define LGW_MODEM1_RAM2_BIST_STATUS 57 +#define LGW_MODEM2_RAM2_BIST_STATUS 58 +#define LGW_MODEM3_RAM2_BIST_STATUS 59 +#define LGW_MODEM4_RAM2_BIST_STATUS 60 +#define LGW_MODEM5_RAM2_BIST_STATUS 61 +#define LGW_MODEM6_RAM2_BIST_STATUS 62 +#define LGW_MODEM7_RAM2_BIST_STATUS 63 +#define LGW_MODEM_MBWSSF_RAM0_BIST_STATUS 64 +#define LGW_MODEM_MBWSSF_RAM1_BIST_STATUS 65 +#define LGW_MODEM_MBWSSF_RAM2_BIST_STATUS 66 +#define LGW_MCU_AGC_DATA_RAM_BIST0_STATUS 67 +#define LGW_MCU_AGC_DATA_RAM_BIST1_STATUS 68 +#define LGW_MCU_ARB_DATA_RAM_BIST0_STATUS 69 +#define LGW_MCU_ARB_DATA_RAM_BIST1_STATUS 70 +#define LGW_TX_TOP_RAM_BIST0_STATUS 71 +#define LGW_TX_TOP_RAM_BIST1_STATUS 72 +#define LGW_DATA_MNGT_RAM_BIST0_STATUS 73 +#define LGW_DATA_MNGT_RAM_BIST1_STATUS 74 +#define LGW_GPIO_SELECT_INPUT 75 +#define LGW_GPIO_SELECT_OUTPUT 76 +#define LGW_GPIO_MODE 77 +#define LGW_GPIO_PIN_REG_IN 78 +#define LGW_GPIO_PIN_REG_OUT 79 +#define LGW_MCU_AGC_STATUS 80 +#define LGW_MCU_ARB_STATUS 81 +#define LGW_CHIP_ID 82 +#define LGW_EMERGENCY_FORCE_HOST_CTRL 83 +#define LGW_RX_INVERT_IQ 84 +#define LGW_MODEM_INVERT_IQ 85 +#define LGW_MBWSSF_MODEM_INVERT_IQ 86 +#define LGW_RX_EDGE_SELECT 87 +#define LGW_MISC_RADIO_EN 88 +#define LGW_FSK_MODEM_INVERT_IQ 89 +#define LGW_FILTER_GAIN 90 +#define LGW_RADIO_SELECT 91 +#define LGW_IF_FREQ_0 92 +#define LGW_IF_FREQ_1 93 +#define LGW_IF_FREQ_2 94 +#define LGW_IF_FREQ_3 95 +#define LGW_IF_FREQ_4 96 +#define LGW_IF_FREQ_5 97 +#define LGW_IF_FREQ_6 98 +#define LGW_IF_FREQ_7 99 +#define LGW_IF_FREQ_8 100 +#define LGW_IF_FREQ_9 101 +#define LGW_CHANN_OVERRIDE_AGC_GAIN 102 +#define LGW_CHANN_AGC_GAIN 103 +#define LGW_CORR0_DETECT_EN 104 +#define LGW_CORR1_DETECT_EN 105 +#define LGW_CORR2_DETECT_EN 106 +#define LGW_CORR3_DETECT_EN 107 +#define LGW_CORR4_DETECT_EN 108 +#define LGW_CORR5_DETECT_EN 109 +#define LGW_CORR6_DETECT_EN 110 +#define LGW_CORR7_DETECT_EN 111 +#define LGW_CORR_SAME_PEAKS_OPTION_SF6 112 +#define LGW_CORR_SAME_PEAKS_OPTION_SF7 113 +#define LGW_CORR_SAME_PEAKS_OPTION_SF8 114 +#define LGW_CORR_SAME_PEAKS_OPTION_SF9 115 +#define LGW_CORR_SAME_PEAKS_OPTION_SF10 116 +#define LGW_CORR_SAME_PEAKS_OPTION_SF11 117 +#define LGW_CORR_SAME_PEAKS_OPTION_SF12 118 +#define LGW_CORR_SIG_NOISE_RATIO_SF6 119 +#define LGW_CORR_SIG_NOISE_RATIO_SF7 120 +#define LGW_CORR_SIG_NOISE_RATIO_SF8 121 +#define LGW_CORR_SIG_NOISE_RATIO_SF9 122 +#define LGW_CORR_SIG_NOISE_RATIO_SF10 123 +#define LGW_CORR_SIG_NOISE_RATIO_SF11 124 +#define LGW_CORR_SIG_NOISE_RATIO_SF12 125 +#define LGW_CORR_NUM_SAME_PEAK 126 +#define LGW_CORR_MAC_GAIN 127 +#define LGW_ADJUST_MODEM_START_OFFSET_RDX4 128 +#define LGW_ADJUST_MODEM_START_OFFSET_SF12_RDX4 129 +#define LGW_DBG_CORR_SELECT_SF 130 +#define LGW_DBG_CORR_SELECT_CHANNEL 131 +#define LGW_DBG_DETECT_CPT 132 +#define LGW_DBG_SYMB_CPT 133 +#define LGW_CHIRP_INVERT_RX 134 +#define LGW_DC_NOTCH_EN 135 +#define LGW_IMPLICIT_CRC_EN 136 +#define LGW_IMPLICIT_CODING_RATE 137 +#define LGW_IMPLICIT_PAYLOAD_LENGHT 138 +#define LGW_FREQ_TO_TIME_INVERT 139 +#define LGW_FREQ_TO_TIME_DRIFT 140 +#define LGW_PAYLOAD_FINE_TIMING_GAIN 141 +#define LGW_PREAMBLE_FINE_TIMING_GAIN 142 +#define LGW_TRACKING_INTEGRAL 143 +#define LGW_FRAME_SYNCH_PEAK1_POS 144 +#define LGW_FRAME_SYNCH_PEAK2_POS 145 +#define LGW_PREAMBLE_SYMB1_NB 146 +#define LGW_FRAME_SYNCH_GAIN 147 +#define LGW_SYNCH_DETECT_TH 148 +#define LGW_LLR_SCALE 149 +#define LGW_SNR_AVG_CST 150 +#define LGW_PPM_OFFSET 151 +#define LGW_MAX_PAYLOAD_LEN 152 +#define LGW_ONLY_CRC_EN 153 +#define LGW_ZERO_PAD 154 +#define LGW_DEC_GAIN_OFFSET 155 +#define LGW_CHAN_GAIN_OFFSET 156 +#define LGW_FORCE_HOST_RADIO_CTRL 157 +#define LGW_FORCE_HOST_FE_CTRL 158 +#define LGW_FORCE_DEC_FILTER_GAIN 159 +#define LGW_MCU_RST_0 160 +#define LGW_MCU_RST_1 161 +#define LGW_MCU_SELECT_MUX_0 162 +#define LGW_MCU_SELECT_MUX_1 163 +#define LGW_MCU_CORRUPTION_DETECTED_0 164 +#define LGW_MCU_CORRUPTION_DETECTED_1 165 +#define LGW_MCU_SELECT_EDGE_0 166 +#define LGW_MCU_SELECT_EDGE_1 167 +#define LGW_CHANN_SELECT_RSSI 168 +#define LGW_RSSI_BB_DEFAULT_VALUE 169 +#define LGW_RSSI_DEC_DEFAULT_VALUE 170 +#define LGW_RSSI_CHANN_DEFAULT_VALUE 171 +#define LGW_RSSI_BB_FILTER_ALPHA 172 +#define LGW_RSSI_DEC_FILTER_ALPHA 173 +#define LGW_RSSI_CHANN_FILTER_ALPHA 174 +#define LGW_IQ_MISMATCH_A_AMP_COEFF 175 +#define LGW_IQ_MISMATCH_A_PHI_COEFF 176 +#define LGW_IQ_MISMATCH_B_AMP_COEFF 177 +#define LGW_IQ_MISMATCH_B_SEL_I 178 +#define LGW_IQ_MISMATCH_B_PHI_COEFF 179 +#define LGW_TX_TRIG_IMMEDIATE 180 +#define LGW_TX_TRIG_DELAYED 181 +#define LGW_TX_TRIG_GPS 182 +#define LGW_TX_START_DELAY 183 +#define LGW_TX_FRAME_SYNCH_PEAK1_POS 184 +#define LGW_TX_FRAME_SYNCH_PEAK2_POS 185 +#define LGW_TX_RAMP_DURATION 186 +#define LGW_TX_OFFSET_I 187 +#define LGW_TX_OFFSET_Q 188 +#define LGW_TX_MODE 189 +#define LGW_TX_ZERO_PAD 190 +#define LGW_TX_EDGE_SELECT 191 +#define LGW_TX_EDGE_SELECT_TOP 192 +#define LGW_TX_GAIN 193 +#define LGW_TX_CHIRP_LOW_PASS 194 +#define LGW_TX_FCC_WIDEBAND 195 +#define LGW_TX_SWAP_IQ 196 +#define LGW_MBWSSF_IMPLICIT_HEADER 197 +#define LGW_MBWSSF_IMPLICIT_CRC_EN 198 +#define LGW_MBWSSF_IMPLICIT_CODING_RATE 199 +#define LGW_MBWSSF_IMPLICIT_PAYLOAD_LENGHT 200 +#define LGW_MBWSSF_AGC_FREEZE_ON_DETECT 201 +#define LGW_MBWSSF_FRAME_SYNCH_PEAK1_POS 202 +#define LGW_MBWSSF_FRAME_SYNCH_PEAK2_POS 203 +#define LGW_MBWSSF_PREAMBLE_SYMB1_NB 204 +#define LGW_MBWSSF_FRAME_SYNCH_GAIN 205 +#define LGW_MBWSSF_SYNCH_DETECT_TH 206 +#define LGW_MBWSSF_DETECT_MIN_SINGLE_PEAK 207 +#define LGW_MBWSSF_DETECT_TRIG_SAME_PEAK_NB 208 +#define LGW_MBWSSF_FREQ_TO_TIME_INVERT 209 +#define LGW_MBWSSF_FREQ_TO_TIME_DRIFT 210 +#define LGW_MBWSSF_PPM_CORRECTION 211 +#define LGW_MBWSSF_PAYLOAD_FINE_TIMING_GAIN 212 +#define LGW_MBWSSF_PREAMBLE_FINE_TIMING_GAIN 213 +#define LGW_MBWSSF_TRACKING_INTEGRAL 214 +#define LGW_MBWSSF_ZERO_PAD 215 +#define LGW_MBWSSF_MODEM_BW 216 +#define LGW_MBWSSF_RADIO_SELECT 217 +#define LGW_MBWSSF_RX_CHIRP_INVERT 218 +#define LGW_MBWSSF_LLR_SCALE 219 +#define LGW_MBWSSF_SNR_AVG_CST 220 +#define LGW_MBWSSF_PPM_OFFSET 221 +#define LGW_MBWSSF_RATE_SF 222 +#define LGW_MBWSSF_ONLY_CRC_EN 223 +#define LGW_MBWSSF_MAX_PAYLOAD_LEN 224 +#define LGW_TX_STATUS 225 +#define LGW_FSK_CH_BW_EXPO 226 +#define LGW_FSK_RSSI_LENGTH 227 +#define LGW_FSK_RX_INVERT 228 +#define LGW_FSK_PKT_MODE 229 +#define LGW_FSK_PSIZE 230 +#define LGW_FSK_CRC_EN 231 +#define LGW_FSK_DCFREE_ENC 232 +#define LGW_FSK_CRC_IBM 233 +#define LGW_FSK_ERROR_OSR_TOL 234 +#define LGW_FSK_RADIO_SELECT 235 +#define LGW_FSK_BR_RATIO 236 +#define LGW_FSK_REF_PATTERN_LSB 237 +#define LGW_FSK_REF_PATTERN_MSB 238 +#define LGW_FSK_PKT_LENGTH 239 +#define LGW_FSK_TX_GAUSSIAN_EN 240 +#define LGW_FSK_TX_GAUSSIAN_SELECT_BT 241 +#define LGW_FSK_TX_PATTERN_EN 242 +#define LGW_FSK_TX_PREAMBLE_SEQ 243 +#define LGW_FSK_TX_PSIZE 244 +#define LGW_FSK_NODE_ADRS 245 +#define LGW_FSK_BROADCAST 246 +#define LGW_FSK_AUTO_AFC_ON 247 +#define LGW_FSK_PATTERN_TIMEOUT_CFG 248 +#define LGW_SPI_RADIO_A__DATA 249 +#define LGW_SPI_RADIO_A__DATA_READBACK 250 +#define LGW_SPI_RADIO_A__ADDR 251 +#define LGW_SPI_RADIO_A__CS 252 +#define LGW_SPI_RADIO_B__DATA 253 +#define LGW_SPI_RADIO_B__DATA_READBACK 254 +#define LGW_SPI_RADIO_B__ADDR 255 +#define LGW_SPI_RADIO_B__CS 256 +#define LGW_RADIO_A_EN 257 +#define LGW_RADIO_B_EN 258 +#define LGW_RADIO_RST 259 +#define LGW_LNA_A_EN 260 +#define LGW_PA_A_EN 261 +#define LGW_LNA_B_EN 262 +#define LGW_PA_B_EN 263 +#define LGW_PA_GAIN 264 +#define LGW_LNA_A_CTRL_LUT 265 +#define LGW_PA_A_CTRL_LUT 266 +#define LGW_LNA_B_CTRL_LUT 267 +#define LGW_PA_B_CTRL_LUT 268 +#define LGW_CAPTURE_SOURCE 269 +#define LGW_CAPTURE_START 270 +#define LGW_CAPTURE_FORCE_TRIGGER 271 +#define LGW_CAPTURE_WRAP 272 +#define LGW_CAPTURE_PERIOD 273 +#define LGW_MODEM_STATUS 274 +#define LGW_VALID_HEADER_COUNTER_0 275 +#define LGW_VALID_PACKET_COUNTER_0 276 +#define LGW_VALID_HEADER_COUNTER_MBWSSF 277 +#define LGW_VALID_HEADER_COUNTER_FSK 278 +#define LGW_VALID_PACKET_COUNTER_MBWSSF 279 +#define LGW_VALID_PACKET_COUNTER_FSK 280 +#define LGW_CHANN_RSSI 281 +#define LGW_BB_RSSI 282 +#define LGW_DEC_RSSI 283 +#define LGW_DBG_MCU_DATA 284 +#define LGW_DBG_ARB_MCU_RAM_DATA 285 +#define LGW_DBG_AGC_MCU_RAM_DATA 286 +#define LGW_NEXT_PACKET_CNT 287 +#define LGW_ADDR_CAPTURE_COUNT 288 +#define LGW_TIMESTAMP 289 +#define LGW_DBG_CHANN0_GAIN 290 +#define LGW_DBG_CHANN1_GAIN 291 +#define LGW_DBG_CHANN2_GAIN 292 +#define LGW_DBG_CHANN3_GAIN 293 +#define LGW_DBG_CHANN4_GAIN 294 +#define LGW_DBG_CHANN5_GAIN 295 +#define LGW_DBG_CHANN6_GAIN 296 +#define LGW_DBG_CHANN7_GAIN 297 +#define LGW_DBG_DEC_FILT_GAIN 298 +#define LGW_SPI_DATA_FIFO_PTR 299 +#define LGW_PACKET_DATA_FIFO_PTR 300 +#define LGW_DBG_ARB_MCU_RAM_ADDR 301 +#define LGW_DBG_AGC_MCU_RAM_ADDR 302 +#define LGW_SPI_MASTER_CHIP_SELECT_POLARITY 303 +#define LGW_SPI_MASTER_CPOL 304 +#define LGW_SPI_MASTER_CPHA 305 +#define LGW_SIG_GEN_ANALYSER_MUX_SEL 306 +#define LGW_SIG_GEN_EN 307 +#define LGW_SIG_ANALYSER_EN 308 +#define LGW_SIG_ANALYSER_AVG_LEN 309 +#define LGW_SIG_ANALYSER_PRECISION 310 +#define LGW_SIG_ANALYSER_VALID_OUT 311 +#define LGW_SIG_GEN_FREQ 312 +#define LGW_SIG_ANALYSER_FREQ 313 +#define LGW_SIG_ANALYSER_I_OUT 314 +#define LGW_SIG_ANALYSER_Q_OUT 315 +#define LGW_GPS_EN 316 +#define LGW_GPS_POL 317 +#define LGW_SW_TEST_REG1 318 +#define LGW_SW_TEST_REG2 319 +#define LGW_SW_TEST_REG3 320 +#define LGW_DATA_MNGT_STATUS 321 +#define LGW_DATA_MNGT_CPT_FRAME_ALLOCATED 322 +#define LGW_DATA_MNGT_CPT_FRAME_FINISHED 323 +#define LGW_DATA_MNGT_CPT_FRAME_READEN 324 +#define LGW_TX_TRIG_ALL 325 + +#define LGW_TOTALREGS 326 + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */ + +/** +@brief Connect LoRa concentrator by opening SPI link +@param spi_only indicates if we only want to create the SPI connexion to the +concentrator, or if we also want to reset it and configure the FPGA (if present) +@param tx_notch_filter TX notch filter frequency to be set in the FPGA (only +used with SX1301AP2 reference design). +@return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR) +*/ +int lgw_connect(bool spi_only, uint32_t tx_notch_freq); + +/** +@brief Disconnect LoRa concentrator by closing SPI link +@return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR) +*/ +int lgw_disconnect(void); + +/** +@brief Use the soft-reset register to put the concentrator in initial state +@return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR) +*/ +int lgw_soft_reset(void); + +/** +@brief Check if the registers are ok, send diagnostics to stdio/stderr/file +@param f file descriptor to to which the check result will be written +@return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR) +*/ +int lgw_reg_check(FILE *f); + +/** +@brief LoRa concentrator register write +@param register_id register number in the data structure describing registers +@param reg_value signed value to write to the register (for u32, use cast) +@return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR) +*/ +int lgw_reg_w(uint16_t register_id, int32_t reg_value); + +/** +@brief LoRa concentrator register read +@param register_id register number in the data structure describing registers +@param reg_value pointer to a variable where to write register read value +@return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR) +*/ +int lgw_reg_r(uint16_t register_id, int32_t *reg_value); + +/** +@brief LoRa concentrator register burst write +@param register_id register number in the data structure describing registers +@param data pointer to byte array that will be sent to the LoRa concentrator +@param size size of the transfer, in byte(s) +@return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR) +*/ +int lgw_reg_wb(uint16_t register_id, uint8_t *data, uint16_t size); + +/** +@brief LoRa concentrator register burst read +@param register_id register number in the data structure describing registers +@param data pointer to byte array that will be written from the LoRa concentrator +@param size size of the transfer, in byte(s) +@return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR) +*/ +int lgw_reg_rb(uint16_t register_id, uint8_t *data, uint16_t size); + + +#endif + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/inc/loragw_spi.h b/libloragw/inc/loragw_spi.h new file mode 100644 index 0000000..fef1f48 --- /dev/null +++ b/libloragw/inc/loragw_spi.h @@ -0,0 +1,105 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Host specific functions to address the LoRa concentrator registers through a + SPI interface. + Single-byte read/write and burst read/write. + Does not handle pagination. + Could be used with multiple SPI ports in parallel (explicit file descriptor) + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +#ifndef _LORAGW_SPI_H +#define _LORAGW_SPI_H + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include <stdint.h> /* C99 types*/ + +#include "config.h" /* library configuration options (dynamically generated) */ + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC CONSTANTS ----------------------------------------------------- */ + +#define LGW_SPI_SUCCESS 0 +#define LGW_SPI_ERROR -1 +#define LGW_BURST_CHUNK 1024 + +#define LGW_SPI_MUX_MODE0 0x0 /* No FPGA */ +#define LGW_SPI_MUX_MODE1 0x1 /* FPGA, with spi mux header */ + +#define LGW_SPI_MUX_TARGET_SX1301 0x0 +#define LGW_SPI_MUX_TARGET_FPGA 0x1 +#define LGW_SPI_MUX_TARGET_EEPROM 0x2 +#define LGW_SPI_MUX_TARGET_SX127X 0x3 + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */ + +/** +@brief LoRa concentrator SPI setup (configure I/O and peripherals) +@param spi_target_ptr pointer on a generic pointer to SPI target (implementation dependant) +@return status of register operation (LGW_SPI_SUCCESS/LGW_SPI_ERROR) +*/ + +int lgw_spi_open(void **spi_target_ptr); + +/** +@brief LoRa concentrator SPI close +@param spi_target generic pointer to SPI target (implementation dependant) +@return status of register operation (LGW_SPI_SUCCESS/LGW_SPI_ERROR) +*/ + +int lgw_spi_close(void *spi_target); + +/** +@brief LoRa concentrator SPI single-byte write +@param spi_target generic pointer to SPI target (implementation dependant) +@param address 7-bit register address +@param data data byte to write +@return status of register operation (LGW_SPI_SUCCESS/LGW_SPI_ERROR) +*/ +int lgw_spi_w(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t data); + +/** +@brief LoRa concentrator SPI single-byte read +@param spi_target generic pointer to SPI target (implementation dependant) +@param address 7-bit register address +@param data data byte to write +@return status of register operation (LGW_SPI_SUCCESS/LGW_SPI_ERROR) +*/ +int lgw_spi_r(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data); + +/** +@brief LoRa concentrator SPI burst (multiple-byte) write +@param spi_target generic pointer to SPI target (implementation dependant) +@param address 7-bit register address +@param data pointer to byte array that will be sent to the LoRa concentrator +@param size size of the transfer, in byte(s) +@return status of register operation (LGW_SPI_SUCCESS/LGW_SPI_ERROR) +*/ +int lgw_spi_wb(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data, uint16_t size); + +/** +@brief LoRa concentrator SPI burst (multiple-byte) read +@param spi_target generic pointer to SPI target (implementation dependant) +@param address 7-bit register address +@param data pointer to byte array that will be written from the LoRa concentrator +@param size size of the transfer, in byte(s) +@return status of register operation (LGW_SPI_SUCCESS/LGW_SPI_ERROR) +*/ +int lgw_spi_rb(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data, uint16_t size); + +#endif + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/inc/loragw_sx125x.h b/libloragw/inc/loragw_sx125x.h new file mode 100644 index 0000000..120ee68 --- /dev/null +++ b/libloragw/inc/loragw_sx125x.h @@ -0,0 +1,49 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: SX125x radio registers and constant definitions + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Michael Coracin +*/ +#ifndef __SX125X_REGS_H__ +#define __SX125X_REGS_H__ + +/* +SX1257 frequency setting : +F_register(24bit) = F_rf (Hz) / F_step(Hz) + = F_rf (Hz) * 2^19 / F_xtal(Hz) + = F_rf (Hz) * 2^19 / 32e6 + = F_rf (Hz) * 256/15625 + +SX1255 frequency setting : +F_register(24bit) = F_rf (Hz) / F_step(Hz) + = F_rf (Hz) * 2^20 / F_xtal(Hz) + = F_rf (Hz) * 2^20 / 32e6 + = F_rf (Hz) * 512/15625 +*/ + +#define SX125x_TX_DAC_CLK_SEL 1 /* 0:int, 1:ext */ +#define SX125x_TX_DAC_GAIN 2 /* 3:0, 2:-3, 1:-6, 0:-9 dBFS (default 2) */ +#define SX125x_TX_MIX_GAIN 14 /* -38 + 2*TxMixGain dB (default 14) */ +#define SX125x_TX_PLL_BW 1 /* 0:75, 1:150, 2:225, 3:300 kHz (default 3) */ +#define SX125x_TX_ANA_BW 0 /* 17.5 / 2*(41-TxAnaBw) MHz (default 0) */ +#define SX125x_TX_DAC_BW 5 /* 24 + 8*TxDacBw Nb FIR taps (default 2) */ +#define SX125x_RX_LNA_GAIN 1 /* 1 to 6, 1 highest gain */ +#define SX125x_RX_BB_GAIN 12 /* 0 to 15 , 15 highest gain */ +#define SX125x_LNA_ZIN 1 /* 0:50, 1:200 Ohms (default 1) */ +#define SX125x_RX_ADC_BW 7 /* 0 to 7, 2:100<BW<200, 5:200<BW<400,7:400<BW kHz SSB (default 7) */ +#define SX125x_RX_ADC_TRIM 6 /* 0 to 7, 6 for 32MHz ref, 5 for 36MHz ref */ +#define SX125x_RX_BB_BW 0 /* 0:750, 1:500, 2:375; 3:250 kHz SSB (default 1, max 3) */ +#define SX125x_RX_PLL_BW 0 /* 0:75, 1:150, 2:225, 3:300 kHz (default 3, max 3) */ +#define SX125x_ADC_TEMP 0 /* ADC temperature measurement mode (default 0) */ +#define SX125x_XOSC_GM_STARTUP 13 /* (default 13) */ +#define SX125x_XOSC_DISABLE 2 /* Disable of Xtal Oscillator blocks bit0:regulator, bit1:core(gm), bit2:amplifier */ + +#endif // __SX125X_REGS_H__ diff --git a/libloragw/inc/loragw_sx1272_fsk.h b/libloragw/inc/loragw_sx1272_fsk.h new file mode 100644 index 0000000..c202bc4 --- /dev/null +++ b/libloragw/inc/loragw_sx1272_fsk.h @@ -0,0 +1,113 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: SX1272 FSK modem registers + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Michael Coracin +*/ +#ifndef _LORAGW_SX1272_REGS_FSK_H +#define _LORAGW_SX1272_REGS_FSK_H + +/*! + * ============================================================================ + * SX1272 Internal registers Address + * ============================================================================ + */ +#define SX1272_REG_FIFO 0x00 +// Common settings +#define SX1272_REG_OPMODE 0x01 +#define SX1272_REG_BITRATEMSB 0x02 +#define SX1272_REG_BITRATELSB 0x03 +#define SX1272_REG_FDEVMSB 0x04 +#define SX1272_REG_FDEVLSB 0x05 +#define SX1272_REG_FRFMSB 0x06 +#define SX1272_REG_FRFMID 0x07 +#define SX1272_REG_FRFLSB 0x08 +// Tx settings +#define SX1272_REG_PACONFIG 0x09 +#define SX1272_REG_PARAMP 0x0A +#define SX1272_REG_OCP 0x0B +// Rx settings +#define SX1272_REG_LNA 0x0C +#define SX1272_REG_RXCONFIG 0x0D +#define SX1272_REG_RSSICONFIG 0x0E +#define SX1272_REG_RSSICOLLISION 0x0F +#define SX1272_REG_RSSITHRESH 0x10 +#define SX1272_REG_RSSIVALUE 0x11 +#define SX1272_REG_RXBW 0x12 +#define SX1272_REG_AFCBW 0x13 +#define SX1272_REG_OOKPEAK 0x14 +#define SX1272_REG_OOKFIX 0x15 +#define SX1272_REG_OOKAVG 0x16 +#define SX1272_REG_RES17 0x17 +#define SX1272_REG_RES18 0x18 +#define SX1272_REG_RES19 0x19 +#define SX1272_REG_AFCFEI 0x1A +#define SX1272_REG_AFCMSB 0x1B +#define SX1272_REG_AFCLSB 0x1C +#define SX1272_REG_FEIMSB 0x1D +#define SX1272_REG_FEILSB 0x1E +#define SX1272_REG_PREAMBLEDETECT 0x1F +#define SX1272_REG_RXTIMEOUT1 0x20 +#define SX1272_REG_RXTIMEOUT2 0x21 +#define SX1272_REG_RXTIMEOUT3 0x22 +#define SX1272_REG_RXDELAY 0x23 +// Oscillator settings +#define SX1272_REG_OSC 0x24 +// Packet handler settings +#define SX1272_REG_PREAMBLEMSB 0x25 +#define SX1272_REG_PREAMBLELSB 0x26 +#define SX1272_REG_SYNCCONFIG 0x27 +#define SX1272_REG_SYNCVALUE1 0x28 +#define SX1272_REG_SYNCVALUE2 0x29 +#define SX1272_REG_SYNCVALUE3 0x2A +#define SX1272_REG_SYNCVALUE4 0x2B +#define SX1272_REG_SYNCVALUE5 0x2C +#define SX1272_REG_SYNCVALUE6 0x2D +#define SX1272_REG_SYNCVALUE7 0x2E +#define SX1272_REG_SYNCVALUE8 0x2F +#define SX1272_REG_PACKETCONFIG1 0x30 +#define SX1272_REG_PACKETCONFIG2 0x31 +#define SX1272_REG_PAYLOADLENGTH 0x32 +#define SX1272_REG_NODEADRS 0x33 +#define SX1272_REG_BROADCASTADRS 0x34 +#define SX1272_REG_FIFOTHRESH 0x35 +// SM settings +#define SX1272_REG_SEQCONFIG1 0x36 +#define SX1272_REG_SEQCONFIG2 0x37 +#define SX1272_REG_TIMERRESOL 0x38 +#define SX1272_REG_TIMER1COEF 0x39 +#define SX1272_REG_TIMER2COEF 0x3A +// Service settings +#define SX1272_REG_IMAGECAL 0x3B +#define SX1272_REG_TEMP 0x3C +#define SX1272_REG_LOWBAT 0x3D +// Status +#define SX1272_REG_IRQFLAGS1 0x3E +#define SX1272_REG_IRQFLAGS2 0x3F +// I/O settings +#define SX1272_REG_DIOMAPPING1 0x40 +#define SX1272_REG_DIOMAPPING2 0x41 +// Version +#define SX1272_REG_VERSION 0x42 +// Additional settings +#define SX1272_REG_AGCREF 0x43 +#define SX1272_REG_AGCTHRESH1 0x44 +#define SX1272_REG_AGCTHRESH2 0x45 +#define SX1272_REG_AGCTHRESH3 0x46 +#define SX1272_REG_PLLHOP 0x4B +#define SX1272_REG_TCXO 0x58 +#define SX1272_REG_PADAC 0x5A +#define SX1272_REG_PLL 0x5C +#define SX1272_REG_PLLLOWPN 0x5E +#define SX1272_REG_FORMERTEMP 0x6C +#define SX1272_REG_BITRATEFRAC 0x70 + +#endif // _LORAGW_SX1272_REGS_FSK_H diff --git a/libloragw/inc/loragw_sx1272_lora.h b/libloragw/inc/loragw_sx1272_lora.h new file mode 100644 index 0000000..7f7a7a5 --- /dev/null +++ b/libloragw/inc/loragw_sx1272_lora.h @@ -0,0 +1,89 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: SX1272 LoRa modem registers and bits definitions + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Michael Coracin +*/ +#ifndef _LORAGW_SX1272_REGS_LORA_H +#define _LORAGW_SX1272_REGS_LORA_H + +/*! + * ============================================================================ + * SX1272 Internal registers Address + * ============================================================================ + */ +#define SX1272_REG_LR_FIFO 0x00 +// Common settings +#define SX1272_REG_LR_OPMODE 0x01 +#define SX1272_REG_LR_FRFMSB 0x06 +#define SX1272_REG_LR_FRFMID 0x07 +#define SX1272_REG_LR_FRFLSB 0x08 +// Tx settings +#define SX1272_REG_LR_PACONFIG 0x09 +#define SX1272_REG_LR_PARAMP 0x0A +#define SX1272_REG_LR_OCP 0x0B +// Rx settings +#define SX1272_REG_LR_LNA 0x0C +// LoRa registers +#define SX1272_REG_LR_FIFOADDRPTR 0x0D +#define SX1272_REG_LR_FIFOTXBASEADDR 0x0E +#define SX1272_REG_LR_FIFORXBASEADDR 0x0F +#define SX1272_REG_LR_FIFORXCURRENTADDR 0x10 +#define SX1272_REG_LR_IRQFLAGSMASK 0x11 +#define SX1272_REG_LR_IRQFLAGS 0x12 +#define SX1272_REG_LR_RXNBBYTES 0x13 +#define SX1272_REG_LR_RXHEADERCNTVALUEMSB 0x14 +#define SX1272_REG_LR_RXHEADERCNTVALUELSB 0x15 +#define SX1272_REG_LR_RXPACKETCNTVALUEMSB 0x16 +#define SX1272_REG_LR_RXPACKETCNTVALUELSB 0x17 +#define SX1272_REG_LR_MODEMSTAT 0x18 +#define SX1272_REG_LR_PKTSNRVALUE 0x19 +#define SX1272_REG_LR_PKTRSSIVALUE 0x1A +#define SX1272_REG_LR_RSSIVALUE 0x1B +#define SX1272_REG_LR_HOPCHANNEL 0x1C +#define SX1272_REG_LR_MODEMCONFIG1 0x1D +#define SX1272_REG_LR_MODEMCONFIG2 0x1E +#define SX1272_REG_LR_SYMBTIMEOUTLSB 0x1F +#define SX1272_REG_LR_PREAMBLEMSB 0x20 +#define SX1272_REG_LR_PREAMBLELSB 0x21 +#define SX1272_REG_LR_PAYLOADLENGTH 0x22 +#define SX1272_REG_LR_PAYLOADMAXLENGTH 0x23 +#define SX1272_REG_LR_HOPPERIOD 0x24 +#define SX1272_REG_LR_FIFORXBYTEADDR 0x25 +#define SX1272_REG_LR_FEIMSB 0x28 +#define SX1272_REG_LR_FEIMID 0x29 +#define SX1272_REG_LR_FEILSB 0x2A +#define SX1272_REG_LR_RSSIWIDEBAND 0x2C +#define SX1272_REG_LR_DETECTOPTIMIZE 0x31 +#define SX1272_REG_LR_INVERTIQ 0x33 +#define SX1272_REG_LR_DETECTIONTHRESHOLD 0x37 +#define SX1272_REG_LR_SYNCWORD 0x39 +#define SX1272_REG_LR_INVERTIQ2 0x3B + +// end of documented register in datasheet +// I/O settings +#define SX1272_REG_LR_DIOMAPPING1 0x40 +#define SX1272_REG_LR_DIOMAPPING2 0x41 +// Version +#define SX1272_REG_LR_VERSION 0x42 +// Additional settings +#define SX1272_REG_LR_AGCREF 0x43 +#define SX1272_REG_LR_AGCTHRESH1 0x44 +#define SX1272_REG_LR_AGCTHRESH2 0x45 +#define SX1272_REG_LR_AGCTHRESH3 0x46 +#define SX1272_REG_LR_PLLHOP 0x4B +#define SX1272_REG_LR_TCXO 0x58 +#define SX1272_REG_LR_PADAC 0x5A +#define SX1272_REG_LR_PLL 0x5C +#define SX1272_REG_LR_PLLLOWPN 0x5E +#define SX1272_REG_LR_FORMERTEMP 0x6C + +#endif // _LORAGW_SX1272_REGS_LORA_H diff --git a/libloragw/inc/loragw_sx1276_fsk.h b/libloragw/inc/loragw_sx1276_fsk.h new file mode 100644 index 0000000..944958a --- /dev/null +++ b/libloragw/inc/loragw_sx1276_fsk.h @@ -0,0 +1,112 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: SX1276 FSK modem registers + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Michael Coracin +*/ +#ifndef _LORAGW_SX1276_REGS_FSK_H +#define _LORAGW_SX1276_REGS_FSK_H + +/*! + * ============================================================================ + * SX1276 Internal registers Address + * ============================================================================ + */ +#define SX1276_REG_FIFO 0x00 +// Common settings +#define SX1276_REG_OPMODE 0x01 +#define SX1276_REG_BITRATEMSB 0x02 +#define SX1276_REG_BITRATELSB 0x03 +#define SX1276_REG_FDEVMSB 0x04 +#define SX1276_REG_FDEVLSB 0x05 +#define SX1276_REG_FRFMSB 0x06 +#define SX1276_REG_FRFMID 0x07 +#define SX1276_REG_FRFLSB 0x08 +// Tx settings +#define SX1276_REG_PACONFIG 0x09 +#define SX1276_REG_PARAMP 0x0A +#define SX1276_REG_OCP 0x0B +// Rx settings +#define SX1276_REG_LNA 0x0C +#define SX1276_REG_RXCONFIG 0x0D +#define SX1276_REG_RSSICONFIG 0x0E +#define SX1276_REG_RSSICOLLISION 0x0F +#define SX1276_REG_RSSITHRESH 0x10 +#define SX1276_REG_RSSIVALUE 0x11 +#define SX1276_REG_RXBW 0x12 +#define SX1276_REG_AFCBW 0x13 +#define SX1276_REG_OOKPEAK 0x14 +#define SX1276_REG_OOKFIX 0x15 +#define SX1276_REG_OOKAVG 0x16 +#define SX1276_REG_RES17 0x17 +#define SX1276_REG_RES18 0x18 +#define SX1276_REG_RES19 0x19 +#define SX1276_REG_AFCFEI 0x1A +#define SX1276_REG_AFCMSB 0x1B +#define SX1276_REG_AFCLSB 0x1C +#define SX1276_REG_FEIMSB 0x1D +#define SX1276_REG_FEILSB 0x1E +#define SX1276_REG_PREAMBLEDETECT 0x1F +#define SX1276_REG_RXTIMEOUT1 0x20 +#define SX1276_REG_RXTIMEOUT2 0x21 +#define SX1276_REG_RXTIMEOUT3 0x22 +#define SX1276_REG_RXDELAY 0x23 +// Oscillator settings +#define SX1276_REG_OSC 0x24 +// Packet handler settings +#define SX1276_REG_PREAMBLEMSB 0x25 +#define SX1276_REG_PREAMBLELSB 0x26 +#define SX1276_REG_SYNCCONFIG 0x27 +#define SX1276_REG_SYNCVALUE1 0x28 +#define SX1276_REG_SYNCVALUE2 0x29 +#define SX1276_REG_SYNCVALUE3 0x2A +#define SX1276_REG_SYNCVALUE4 0x2B +#define SX1276_REG_SYNCVALUE5 0x2C +#define SX1276_REG_SYNCVALUE6 0x2D +#define SX1276_REG_SYNCVALUE7 0x2E +#define SX1276_REG_SYNCVALUE8 0x2F +#define SX1276_REG_PACKETCONFIG1 0x30 +#define SX1276_REG_PACKETCONFIG2 0x31 +#define SX1276_REG_PAYLOADLENGTH 0x32 +#define SX1276_REG_NODEADRS 0x33 +#define SX1276_REG_BROADCASTADRS 0x34 +#define SX1276_REG_FIFOTHRESH 0x35 +// SM settings +#define SX1276_REG_SEQCONFIG1 0x36 +#define SX1276_REG_SEQCONFIG2 0x37 +#define SX1276_REG_TIMERRESOL 0x38 +#define SX1276_REG_TIMER1COEF 0x39 +#define SX1276_REG_TIMER2COEF 0x3A +// Service settings +#define SX1276_REG_IMAGECAL 0x3B +#define SX1276_REG_TEMP 0x3C +#define SX1276_REG_LOWBAT 0x3D +// Status +#define SX1276_REG_IRQFLAGS1 0x3E +#define SX1276_REG_IRQFLAGS2 0x3F +// I/O settings +#define SX1276_REG_DIOMAPPING1 0x40 +#define SX1276_REG_DIOMAPPING2 0x41 +// Version +#define SX1276_REG_VERSION 0x42 +// Additional settings +#define SX1276_REG_PLLHOP 0x44 +#define SX1276_REG_TCXO 0x4B +#define SX1276_REG_PADAC 0x4D +#define SX1276_REG_FORMERTEMP 0x5B +#define SX1276_REG_BITRATEFRAC 0x5D +#define SX1276_REG_AGCREF 0x61 +#define SX1276_REG_AGCTHRESH1 0x62 +#define SX1276_REG_AGCTHRESH2 0x63 +#define SX1276_REG_AGCTHRESH3 0x64 +#define SX1276_REG_PLL 0x70 + +#endif // __SX1276_REGS_FSK_H__ diff --git a/libloragw/inc/loragw_sx1276_lora.h b/libloragw/inc/loragw_sx1276_lora.h new file mode 100644 index 0000000..d03e1ff --- /dev/null +++ b/libloragw/inc/loragw_sx1276_lora.h @@ -0,0 +1,94 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + +Description: SX1276 LoRa modem registers + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Michael Coracin +*/ +#ifndef _LORAGW_SX1276_REGS_LORA_H +#define _LORAGW_SX1276_REGS_LORA_H + +/*! + * ============================================================================ + * SX1276 Internal registers Address + * ============================================================================ + */ +#define SX1276_REG_LR_FIFO 0x00 +// Common settings +#define SX1276_REG_LR_OPMODE 0x01 +#define SX1276_REG_LR_FRFMSB 0x06 +#define SX1276_REG_LR_FRFMID 0x07 +#define SX1276_REG_LR_FRFLSB 0x08 +// Tx settings +#define SX1276_REG_LR_PACONFIG 0x09 +#define SX1276_REG_LR_PARAMP 0x0A +#define SX1276_REG_LR_OCP 0x0B +// Rx settings +#define SX1276_REG_LR_LNA 0x0C +// LoRa registers +#define SX1276_REG_LR_FIFOADDRPTR 0x0D +#define SX1276_REG_LR_FIFOTXBASEADDR 0x0E +#define SX1276_REG_LR_FIFORXBASEADDR 0x0F +#define SX1276_REG_LR_FIFORXCURRENTADDR 0x10 +#define SX1276_REG_LR_IRQFLAGSMASK 0x11 +#define SX1276_REG_LR_IRQFLAGS 0x12 +#define SX1276_REG_LR_RXNBBYTES 0x13 +#define SX1276_REG_LR_RXHEADERCNTVALUEMSB 0x14 +#define SX1276_REG_LR_RXHEADERCNTVALUELSB 0x15 +#define SX1276_REG_LR_RXPACKETCNTVALUEMSB 0x16 +#define SX1276_REG_LR_RXPACKETCNTVALUELSB 0x17 +#define SX1276_REG_LR_MODEMSTAT 0x18 +#define SX1276_REG_LR_PKTSNRVALUE 0x19 +#define SX1276_REG_LR_PKTRSSIVALUE 0x1A +#define SX1276_REG_LR_RSSIVALUE 0x1B +#define SX1276_REG_LR_HOPCHANNEL 0x1C +#define SX1276_REG_LR_MODEMCONFIG1 0x1D +#define SX1276_REG_LR_MODEMCONFIG2 0x1E +#define SX1276_REG_LR_SYMBTIMEOUTLSB 0x1F +#define SX1276_REG_LR_PREAMBLEMSB 0x20 +#define SX1276_REG_LR_PREAMBLELSB 0x21 +#define SX1276_REG_LR_PAYLOADLENGTH 0x22 +#define SX1276_REG_LR_PAYLOADMAXLENGTH 0x23 +#define SX1276_REG_LR_HOPPERIOD 0x24 +#define SX1276_REG_LR_FIFORXBYTEADDR 0x25 +#define SX1276_REG_LR_MODEMCONFIG3 0x26 +#define SX1276_REG_LR_FEIMSB 0x28 +#define SX1276_REG_LR_FEIMID 0x29 +#define SX1276_REG_LR_FEILSB 0x2A +#define SX1276_REG_LR_RSSIWIDEBAND 0x2C +#define SX1276_REG_LR_TEST2F 0x2F +#define SX1276_REG_LR_TEST30 0x30 +#define SX1276_REG_LR_DETECTOPTIMIZE 0x31 +#define SX1276_REG_LR_INVERTIQ 0x33 +#define SX1276_REG_LR_TEST36 0x36 +#define SX1276_REG_LR_DETECTIONTHRESHOLD 0x37 +#define SX1276_REG_LR_SYNCWORD 0x39 +#define SX1276_REG_LR_TEST3A 0x3A +#define SX1276_REG_LR_INVERTIQ2 0x3B + +// end of documented register in datasheet +// I/O settings +#define SX1276_REG_LR_DIOMAPPING1 0x40 +#define SX1276_REG_LR_DIOMAPPING2 0x41 +// Version +#define SX1276_REG_LR_VERSION 0x42 +// Additional settings +#define SX1276_REG_LR_PLLHOP 0x44 +#define SX1276_REG_LR_TCXO 0x4B +#define SX1276_REG_LR_PADAC 0x4D +#define SX1276_REG_LR_FORMERTEMP 0x5B +#define SX1276_REG_LR_BITRATEFRAC 0x5D +#define SX1276_REG_LR_AGCREF 0x61 +#define SX1276_REG_LR_AGCTHRESH1 0x62 +#define SX1276_REG_LR_AGCTHRESH2 0x63 +#define SX1276_REG_LR_AGCTHRESH3 0x64 +#define SX1276_REG_LR_PLL 0x70 + +#endif // _LORAGW_SX1276_REGS_LORA_H diff --git a/libloragw/library.cfg b/libloragw/library.cfg new file mode 100644 index 0000000..f962ba2 --- /dev/null +++ b/libloragw/library.cfg @@ -0,0 +1,12 @@ +# That file will be included in the Makefile files that have hardware dependencies + +### Debug options ### +# Set the DEBUG_* to 1 to activate debug mode in individual modules. +# Warning: that makes the module *very verbose*, do not use for production + +DEBUG_AUX= 0 +DEBUG_SPI= 0 +DEBUG_REG= 0 +DEBUG_HAL= 0 +DEBUG_LBT= 0 +DEBUG_GPS= 0 diff --git a/libloragw/readme.md b/libloragw/readme.md new file mode 100644 index 0000000..bdbf97f --- /dev/null +++ b/libloragw/readme.md @@ -0,0 +1,457 @@ + / _____) _ | | + ( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | + (______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +LoRa concentrator HAL user manual +============================ + +1. Introduction +--------------- + +The LoRa concentrator Hardware Abstraction Layer is a C library that allow you +to use a Semtech concentrator chip through a reduced number of high level C +functions to configure the hardware, send and receive packets. + +The Semtech LoRa concentrator is a digital multi-channel multi-standard packet +radio used to send and receive packets wirelessly using LoRa or FSK modulations. + +2. Components of the library +---------------------------- + +The library is composed of 6(8) modules: + +* loragw_hal +* loragw_reg +* loragw_spi +* loragw_aux +* loragw_gps +* loragw_radio +* loragw_fpga (only for SX1301AP2 ref design) +* loragw_lbt (only for SX1301AP2 ref design) + +The library also contains basic test programs to demonstrate code use and check +functionality. + +### 2.1. loragw_hal ### + +This is the main module and contains the high level functions to configure and +use the LoRa concentrator: + +* lgw_board_setconf, to set the configuration of the concentrator +* lgw_rxrf_setconf, to set the configuration of the radio channels +* lgw_rxif_setconf, to set the configuration of the IF+modem channels +* lgw_txgain_setconf, to set the configuration of the concentrator gain table +* lgw_start, to apply the set configuration to the hardware and start it +* lgw_stop, to stop the hardware +* lgw_receive, to fetch packets if any was received +* lgw_send, to send a single packet (non-blocking, see warning in usage section) +* lgw_status, to check when a packet has effectively been sent + +For an standard application, include only this module. +The use of this module is detailed on the usage section. + +/!\ When sending a packet, there is a delay (approx 1.5ms) for the analog +circuitry to start and be stable. This delay is adjusted by the HAL depending +on the board version (lgw_i_tx_start_delay_us). + +In 'timestamp' mode, this is transparent: the modem is started +lgw_i_tx_start_delay_us microseconds before the user-set timestamp value is +reached, the preamble of the packet start right when the internal timestamp +counter reach target value. + +In 'immediate' mode, the packet is emitted as soon as possible: transferring the +packet (and its parameters) from the host to the concentrator takes some time, +then there is the lgw_i_tx_start_delay_us, then the packet is emitted. + +In 'triggered' mode (aka PPS/GPS mode), the packet, typically a beacon, is +emitted lgw_i_tx_start_delay_us microsenconds after a rising edge of the +trigger signal. Because there is no way to anticipate the triggering event and +start the analog circuitry beforehand, that delay must be taken into account in +the protocol. + +### 2.2. loragw_reg ### + +This module is used to access to the LoRa concentrator registers by name instead +of by address: + +* lgw_connect, to initialise and check the connection with the hardware +* lgw_disconnect, to disconnect the hardware +* lgw_soft_reset, to reset the whole hardware by resetting the register array +* lgw_reg_check, to check all registers vs. their default value and output the +result to a file +* lgw_reg_r, read a named register +* lgw_reg_w, write a named register +* lgw_reg_rb, read a name register in burst +* lgw_reg_wb, write a named register in burst + +This module handles pagination, read-only registers protection, multi-byte +registers management, signed registers management, read-modify-write routines +for sub-byte registers and read/write burst fragmentation to respect SPI +maximum burst length constraints. + +It make the code much easier to read and to debug. +Moreover, if registers are relocated between different hardware revisions but +keep the same function, the code written using register names can be reused "as +is". + +If you need access to all the registers, include this module in your +application. + +**/!\ Warning** please be sure to have a good understanding of the LoRa +concentrator inner working before accessing the internal registers directly. + +### 2.3. loragw_spi ### + +This module contains the functions to access the LoRa concentrator register +array through the SPI interface: + +* lgw_spi_r to read one byte +* lgw_spi_w to write one byte +* lgw_spi_rb to read two bytes or more +* lgw_spi_wb to write two bytes or more + +Please *do not* include that module directly into your application. + +**/!\ Warning** Accessing the LoRa concentrator register array without the +checks and safety provided by the functions in loragw_reg is not recommended. + +### 2.4. loragw_aux ### + +This module contains a single host-dependant function wait_ms to pause for a +defined amount of milliseconds. + +The procedure to start and configure the LoRa concentrator hardware contained in +the loragw_hal module requires to wait for several milliseconds at certain +steps, typically to allow for supply voltages or clocks to stabilize after been +switched on. + +An accuracy of 1 ms or less is ideal. +If your system does not allow that level of accuracy, make sure that the actual +delay is *longer* that the time specified when the function is called (ie. +wait_ms(X) **MUST NOT** before X milliseconds under any circumstance). + +If the minimum delays are not guaranteed during the configuration and start +procedure, the hardware might not work at nominal performance. +Most likely, it will not work at all. + +### 2.5. loragw_gps ### + +This module contains functions to synchronize the concentrator internal +counter with an absolute time reference, in our case a GPS satellite receiver. + +The internal concentrator counter is used to timestamp incoming packets and to +triggers outgoing packets with a microsecond accuracy. +In some cases, it might be useful to be able to transform that internal +timestamp (that is independent for each concentrator running in a typical +networked system) into an absolute GPS time. + +In a typical implementation a GPS specific thread will be called, doing the +following things after opening the serial port: + +* blocking reads on the serial port (using system read() function) +* parse UBX messages (using lgw_parse_ubx) to get actual native GPS time +* parse NMEA sentences (using lgw_parse_nmea) to get location and UTC time +Note: the RMC sentence gives UTC time, not native GPS time. + +And each time an NAV-TIMEGPS UBX message has been received: + +* get the concentrator timestamp (using lgw_get_trigcnt, mutex needed to + protect access to the concentrator) +* get the GPS time contained in the UBX message (using lgw_gps_get) +* call the lgw_gps_sync function (use mutex to protect the time reference that + should be a global shared variable). + +Then, in other threads, you can simply used that continuously adjusted time +reference to convert internal timestamps to GPS time (using lgw_cnt2gps) or +the other way around (using lgw_gps2cnt). Inernal concentrator timestamp can +also be converted to/from UTC time using lgw_cnt2utc/lgw_utc2cnt functions. + +### 2.6. loragw_radio ### + +This module contains functions to handle the configuration of SX125x and +SX127x radios. + +### 2.7. loragw_fpga ### + +This module contains the description of the FPGA registers, the functions to +read/write those registers, and a function to configure the FPGA features. + +This module is only required for SX1301AP2 reference design. + +### 2.8. loragw_lbt ### + +This module contains functions to configure and use the "Listen-Before-Talk" +feature (refered as LBT below). It depends on the loragw_fpga and loragw_radio +modules. + +LBT feature is only available on SX1301AP2 reference design, which provides the +FPGA and the SX127x radio required to accomplish the feature. + +The FPGA implements the following Finite State Machine (FSM) to scan the defined +LBT channels (8 max), and also compute the RSSI histogram for spectral scan, +using the SX127x radio. + + + +-------+ + +------------------>+ idle +------------------+ + | +-------+ v + | | +-----------+ + | | | clean mem | + | v +-----------+ + | +----------+ | + | | set freq |<---------------+ + | +----------+ + | | + | v + | +----------+ + | | wait pll | + | | lock | + | +----------+ + | | (SCAN_CHANNEL) + | v +-----------+ + | +-----------+ | | + | | +----------+ v + | +-->| read RSSI | +------------+ + | | | +<---------------+ calc histo | + | | +-----------+ SCANNING +------------+ + | | | | + | SCANNING | | (LBT_CHANNEL) | + | | v | + | | +-------------+ | + | | | compare | | + | +--+ with | | + | | RSSI_TARGET | HISTO_DONE | + | +-------------+ | + | | | + | SCAN DONE | | + | v | + | +------------+ | + | | increase | | + +-----------------+ +<--------------------+ + | freq | + +------------+ + + + +In order to configure the LBT, the following parameters have to be set: +- RSSI_TARGET: signal strength target used to detect if the channel is clear + or not. + RSSI_TARGET_dBm = -RSSI_TARGET/2 +- LBT_CHx_FREQ_OFFSET: with x=[0..7], offset from the predefined LBT start + frequency (863MHz or 915MHz depending on FPGA image), + in 100KHz unit. +- LBT_SCAN_TIME_CHx: with x=[0..7], the channel scan time to be used for this + LBT channel: 128µs or 5000µs + +With this FSM, the FPGA keeps the last instant when each channel was free during +more than LBT_SCAN_TIME_CHx µs. + +Then, the HAL, when receiving a downlink request, will first determine on which +LBT channel this downlink is supposed to be sent and then checks if the channel +is busy or if downlink is allowed. + +In order to determine if a downlink is allowed or not, the HAL does: +- read the LBT_TIMESTAMP_CH of the channel on which downlink is requested. This + gives the last time when channel was free (LBT_TIME). +- compute the time on air of the downlink packet to determine the end time of + the packet emission (PKT_END_TIME). +- if ((PKT_END_TIME - LBT_TIME) < TX_MAX_TIME) + ALLOWED = TRUE + else + ALLOWED = FALSE + endif + where TX_MAX_TIME is the maximum time allowed to send a packet since the + last channel free time (this depends on the channel scan time ). + + +3. Software build process +-------------------------- + +### 3.1. Details of the software ### + +The library is written following ANSI C conventions but using C99 explicit +length data type for all data exchanges with hardware and for parameters. + +The loragw_aux module contains POSIX dependant functions for millisecond +accuracy pause. +For embedded platforms, the function could be rewritten using hardware timers. + +### 3.2. Building options ### + +All modules use a fprintf(stderr,...) function to display debug diagnostic +messages if the DEBUG_xxx is set to 1 in library.cfg + +### 3.3. Building procedures ### + +For cross-compilation set the ARCH and CROSS_COMPILE variables in the Makefile, +or in your shell environment, with the correct toolchain name and path. +ex: +export PATH=/home/foo/rpi-toolchain/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin:$PATH +export ARCH=arm +export CROSS_COMPILE=arm-linux-gnueabihf- + +The Makefile in the libloragw directory will parse the library.cfg file and +generate a config.h C header file containing #define options. +Those options enables and disables sections of code in the loragw_xxx.h files +and the *.c source files. + +The library.cfg is also used directly to select the proper set of dynamic +libraries to be linked with. + +### 3.4. Export ### + +Once build, to use that library on another system, you need to export the +following files : + +* libloragw/library.cfg -> root configuration file +* libloragw/libloragw.a -> static library, to be linked with a program +* libloragw/readme.md -> required for license compliance +* libloragw/inc/config.h -> C configuration flags, derived from library.cfg +* libloragw/inc/loragw_*.h -> take only the ones you need (eg. _hal and _gps) + +After statically linking the library to your application, only the license +is required to be kept or copied inside your program documentation. + +4. Hardware dependencies +------------------------ + +### 4.1. Hardware revision ### + +The loragw_reg and loragw_hal are written for a specific version on the Semtech +hardware (IP and/or silicon revision). + +This code has been written for: + +* Semtech SX1301 chip +* Semtech SX1257 or SX1255 I/Q transceivers + +The library will not work if there is a mismatch between the hardware version +and the library version. You can use the test program test_loragw_reg to check +if the hardware registers match their software declaration. + +### 4.2. SPI communication ### + +loragw_spi contains 4 SPI functions (read, write, burst read, burst write) that +are platform-dependant. +The functions must be rewritten depending on the SPI bridge you use: + +* SPI master matched to the Linux SPI device driver (provided) +* SPI over USB using FTDI components (not provided) +* native SPI using a microcontroller peripheral (not provided) + +You can use the test program test_loragw_spi to check with a logic analyser +that the SPI communication is working + +### 4.3. GPS receiver (or other GNSS system) ### + +To use the GPS module of the library, the host must be connected to a GPS +receiver via a serial link (or an equivalent receiver using a different +satellite constellation). +The serial link must appear as a "tty" device in the /dev/ directory, and the +user launching the program must have the proper system rights to read and +write on that device. +Use `chmod a+rw` to allow all users to access that specific tty device, or use +sudo to run all your programs (eg. `sudo ./test_loragw_gps`). + +In the current revision, the library only reads data from the serial port, +expecting to receive NMEA frames that are generally sent by GPS receivers as +soon as they are powered up, and UBX messages which are proprietary to u-blox +modules. + +The GPS receiver **MUST** send UBX messages shortly after sending a PPS pulse +on to allow internal concentrator timestamps to be converted to absolute GPS time. +If the GPS receiver sends a GGA NMEA sentence, the gateway 3D position will +also be available. + +5. Usage +-------- + +### 5.1. Setting the software environment ### + +For a typical application you need to: + +* include loragw_hal.h in your program source +* link to the libloragw.a static library during compilation +* link to the librt library due to loragw_aux dependencies (timing functions) + +For an application that will also access the concentrator configuration +registers directly (eg. for advanced configuration) you also need to: + +* include loragw_reg.h in your program source + +### 5.2. Using the software API ### + +To use the HAL in your application, you must follow some basic rules: + +* configure the radios path and IF+modem path before starting the radio +* the configuration is only transferred to hardware when you call the *start* + function +* you cannot receive packets until one (or +) radio is enabled AND one (or +) + IF+modem part is enabled AND the concentrator is started +* you cannot send packets until one (or +) radio is enabled AND the concentrator + is started +* you must stop the concentrator before changing the configuration + +A typical application flow for using the HAL is the following: + + <configure the radios and IF+modems> + <start the LoRa concentrator> + loop { + <fetch packets that were received by the concentrator> + <process, store and/or forward received packets> + <send packets through the concentrator> + } + <stop the concentrator> + +**/!\ Warning** The lgw_send function is non-blocking and returns while the +LoRa concentrator is still sending the packet, or even before the packet has +started to be transmitted if the packet is triggered on a future event. +While a packet is emitted, no packet can be received (limitation intrinsic to +most radio frequency systems). + +Your application *must* take into account the time it takes to send a packet or +check the status (using lgw_status) before attempting to send another packet. + +Trying to send a packet while the previous packet has not finished being send +will result in the previous packet not being sent or being sent only partially +(resulting in a CRC error in the receiver). + +### 5.3. Debugging mode ### + +To debug your application, it might help to compile the loragw_hal function +with the debug messages activated (set DEBUG_HAL=1 in library.cfg). +It then send a lot of details, including detailed error messages to *stderr*. + +6. License +----------- + +Copyright (c) 2013, SEMTECH S.A. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of the Semtech corporation nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*EOF* diff --git a/libloragw/src/agc_fw.var b/libloragw/src/agc_fw.var new file mode 100644 index 0000000..5083a0e --- /dev/null +++ b/libloragw/src/agc_fw.var @@ -0,0 +1,529 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + AGC firmware + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Matthieu Leurent +*/ + +static uint8_t agc_firmware[MCU_AGC_FW_BYTE] = { +0x8A, 0x51, 0x11, 0x28, 0xFF, 0xBF, 0xFF, 0xBF, 0x80, 0x40, 0x03, 0x4E, 0x83, 0x52, 0x03, 0x53, +0xAC, 0x00, 0x04, 0x88, 0xAD, 0x40, 0x0A, 0xC8, 0xAE, 0x40, 0x01, 0x88, 0xAF, 0x80, 0x8A, 0x51, +0x13, 0x68, 0x8A, 0x51, 0x59, 0x2D, 0x8B, 0xDC, 0x1A, 0x68, 0xA0, 0xE0, 0x8A, 0x51, 0x27, 0x60, +0x40, 0xF0, 0x9B, 0x40, 0x10, 0xF0, 0x8B, 0x00, 0x2F, 0x88, 0x81, 0x80, 0x2E, 0x48, 0x8A, 0xC0, +0x2D, 0x48, 0x84, 0x80, 0x2C, 0x8E, 0x83, 0xC0, 0x80, 0x0E, 0x00, 0xCE, 0x09, 0x80, 0x95, 0x41, +0x96, 0x41, 0x97, 0x81, 0x98, 0x01, 0x99, 0x41, 0x9A, 0x41, 0x9B, 0x81, 0x9C, 0x41, 0x9E, 0x81, +0x19, 0x54, 0x19, 0x95, 0x18, 0x56, 0x8B, 0x41, 0xD4, 0x41, 0x02, 0xF0, 0x54, 0x02, 0x03, 0x18, +0x59, 0xA8, 0x54, 0x08, 0x51, 0x3E, 0x84, 0x80, 0x83, 0x93, 0x80, 0x81, 0x54, 0x08, 0x5D, 0xBE, +0x84, 0x80, 0x07, 0x70, 0x80, 0x40, 0x54, 0x08, 0x72, 0x60, 0x8A, 0x51, 0x54, 0x08, 0x5D, 0xBE, +0x84, 0x80, 0x00, 0xCE, 0xF0, 0x39, 0x96, 0x00, 0x54, 0x9C, 0x52, 0x68, 0x83, 0x52, 0x03, 0x53, +0x18, 0x14, 0x55, 0xA8, 0x83, 0x52, 0x03, 0x53, 0x18, 0xD0, 0x18, 0x55, 0x18, 0x11, 0xD4, 0x8A, +0x35, 0xA8, 0xD3, 0x81, 0x08, 0xF0, 0x53, 0x42, 0x03, 0x18, 0x6F, 0x28, 0x53, 0x48, 0x55, 0x7E, +0x84, 0x80, 0x04, 0xF0, 0x83, 0x93, 0x80, 0x40, 0x53, 0x48, 0x55, 0x7E, 0x84, 0x80, 0x00, 0x48, +0x96, 0x00, 0x53, 0x48, 0x95, 0x00, 0x98, 0x54, 0x98, 0x10, 0xD3, 0xCA, 0x5A, 0xA8, 0x10, 0xF0, +0x9B, 0x40, 0x08, 0x40, 0xAB, 0x40, 0x51, 0x3E, 0x84, 0x80, 0x00, 0x48, 0xAA, 0x00, 0x23, 0x3E, +0x84, 0x80, 0x8A, 0x51, 0x1D, 0xE5, 0x8A, 0x51, 0xA7, 0x40, 0x05, 0x30, 0x03, 0xD0, 0xA7, 0x0D, +0xFF, 0x7E, 0x03, 0x9D, 0x7E, 0x28, 0x2A, 0x08, 0x2D, 0x7E, 0x84, 0x80, 0x8A, 0x51, 0x1D, 0xE5, +0x8A, 0x51, 0xA8, 0xC0, 0x28, 0x47, 0x27, 0x44, 0x01, 0x38, 0xA9, 0x00, 0x2B, 0x48, 0x22, 0xFE, +0x84, 0x80, 0x29, 0x08, 0x80, 0x40, 0x2B, 0x48, 0x22, 0xFE, 0x84, 0x80, 0x00, 0x48, 0x83, 0x96, +0xA0, 0x80, 0x83, 0x52, 0x2B, 0x48, 0x83, 0x96, 0xA1, 0xC0, 0x0C, 0x30, 0xD3, 0xE1, 0x08, 0x40, +0x18, 0x12, 0x80, 0xF0, 0x9B, 0x40, 0x10, 0xF0, 0x9E, 0x40, 0x13, 0x1F, 0xAB, 0xE8, 0x83, 0x52, +0x03, 0x53, 0x18, 0x14, 0xAE, 0xE8, 0x83, 0x52, 0x03, 0x53, 0x18, 0xD0, 0x92, 0x1F, 0xB4, 0xA8, +0x83, 0x96, 0x03, 0x53, 0xA4, 0x54, 0xB7, 0x28, 0x83, 0x96, 0x03, 0x53, 0xA4, 0x10, 0x83, 0x52, +0x12, 0xDF, 0xBE, 0x28, 0x83, 0x96, 0x03, 0x53, 0x24, 0x14, 0xC1, 0x68, 0x83, 0x96, 0x03, 0x53, +0x24, 0xD0, 0x83, 0x52, 0x5F, 0xC8, 0xA7, 0x40, 0x06, 0x30, 0x03, 0xD0, 0xA7, 0x0D, 0xFF, 0x7E, +0x03, 0x9D, 0xC5, 0xA8, 0x12, 0xC8, 0x3F, 0xB9, 0x27, 0x44, 0xA5, 0x00, 0x83, 0x96, 0x24, 0x5C, +0xD3, 0xE8, 0x83, 0x52, 0x9E, 0x15, 0x83, 0x52, 0x13, 0x08, 0x0F, 0x39, 0x3F, 0xFE, 0x84, 0x80, +0x83, 0x93, 0x00, 0x48, 0xA1, 0xC0, 0x21, 0xC8, 0xA9, 0x00, 0x3F, 0x30, 0xA9, 0x85, 0x29, 0x08, +0x83, 0x96, 0xA0, 0x80, 0x00, 0xB0, 0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, 0xA1, 0xC0, +0x08, 0xF0, 0xD3, 0xE1, 0x8A, 0x51, 0x21, 0xDF, 0xF1, 0xE8, 0x83, 0x52, 0x03, 0x53, 0x19, 0x96, +0xF4, 0xE8, 0x83, 0x52, 0x03, 0x53, 0x19, 0x52, 0xA1, 0x1F, 0xFA, 0x28, 0x83, 0x52, 0x03, 0x53, +0x99, 0xD6, 0xFD, 0x68, 0x83, 0x52, 0x03, 0x53, 0x99, 0x92, 0x83, 0x96, 0xA4, 0x9C, 0x0D, 0xA9, +0x6C, 0xB0, 0xA0, 0x80, 0x00, 0xB0, 0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, 0xA1, 0xC0, +0x0A, 0x30, 0xD3, 0xE1, 0x8A, 0x51, 0x04, 0xF0, 0x19, 0xA9, 0x60, 0x30, 0xA0, 0x80, 0x00, 0xB0, +0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, 0xA1, 0xC0, 0x0A, 0x30, 0xD3, 0xE1, 0x8A, 0x51, +0x05, 0x30, 0x83, 0x96, 0xA0, 0x80, 0x00, 0xB0, 0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, +0xA1, 0xC0, 0x0B, 0x70, 0xD3, 0xE1, 0x8A, 0x51, 0x25, 0x08, 0x83, 0x96, 0xA0, 0x80, 0x00, 0xB0, +0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, 0xA1, 0xC0, 0x04, 0xF0, 0xD3, 0xE1, 0x8A, 0x51, +0x11, 0xC8, 0x83, 0x96, 0xA0, 0x80, 0x00, 0xB0, 0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, +0xA1, 0xC0, 0x05, 0x30, 0xD3, 0xE1, 0x8A, 0x51, 0x10, 0x88, 0x83, 0x96, 0xA0, 0x80, 0x00, 0xB0, +0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, 0xA1, 0xC0, 0x06, 0x30, 0xD3, 0xE1, 0x8A, 0x51, +0x83, 0x96, 0x00, 0xB0, 0xA0, 0xC1, 0xA0, 0x0A, 0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, +0xA1, 0xC0, 0x00, 0xB0, 0xD3, 0xE1, 0x8A, 0x51, 0x35, 0xB0, 0xA7, 0x40, 0xA7, 0x0B, 0x56, 0xE9, +0x05, 0x30, 0x83, 0x96, 0x03, 0x53, 0xA0, 0x80, 0x00, 0xB0, 0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, +0x83, 0x96, 0xA1, 0xC0, 0x00, 0xB0, 0xD3, 0xE1, 0x8A, 0x51, 0x03, 0x30, 0xA8, 0xC0, 0x7D, 0x30, +0xA7, 0x40, 0xA7, 0x0B, 0x69, 0xE9, 0xA8, 0x8B, 0x69, 0xE9, 0x83, 0x52, 0x03, 0x53, 0x13, 0x1F, +0x73, 0x29, 0x19, 0x51, 0x74, 0xE9, 0x19, 0x10, 0x05, 0x30, 0xA7, 0x40, 0xA7, 0x0B, 0x76, 0x29, +0x83, 0x52, 0x03, 0x53, 0x13, 0x1F, 0x7E, 0x69, 0x99, 0xD5, 0x7F, 0xA9, 0x99, 0x94, 0x15, 0x70, +0xA8, 0xC0, 0xC6, 0xB0, 0xA7, 0x40, 0xA7, 0x0B, 0x83, 0xA9, 0xA8, 0x8B, 0x83, 0xA9, 0x00, 0x00, +0x0D, 0x70, 0x83, 0x96, 0x03, 0x53, 0xA0, 0x80, 0x00, 0xB0, 0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, +0x83, 0x96, 0xA1, 0xC0, 0x00, 0xB0, 0xD3, 0xE1, 0x8A, 0x51, 0x93, 0x1B, 0x95, 0xE9, 0x99, 0x92, +0x19, 0x52, 0x83, 0x96, 0x00, 0xB0, 0xA0, 0xC1, 0xA0, 0x0A, 0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, +0x83, 0x96, 0xA1, 0xC0, 0x00, 0xB0, 0xD3, 0xE1, 0x8A, 0x51, 0x13, 0x1F, 0xA9, 0xE9, 0x99, 0x91, +0xAA, 0xE9, 0x99, 0x50, 0xE4, 0xB0, 0xA7, 0x40, 0xAD, 0x29, 0xAE, 0x29, 0xA7, 0x0B, 0xAC, 0xE9, +0xB1, 0xE9, 0x00, 0x00, 0x83, 0x52, 0x03, 0x53, 0x13, 0x1F, 0xB8, 0xE9, 0x19, 0x95, 0xB9, 0x29, +0x19, 0x54, 0x03, 0x30, 0x83, 0x96, 0xA0, 0x80, 0x00, 0xB0, 0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, +0x83, 0x96, 0xA1, 0xC0, 0x00, 0xB0, 0xD3, 0xE1, 0x8A, 0x51, 0x05, 0x30, 0xA8, 0xC0, 0x26, 0x70, +0xA7, 0x40, 0xA7, 0x0B, 0xC9, 0xE9, 0xA8, 0x8B, 0xC9, 0xE9, 0x00, 0x00, 0x83, 0x52, 0x03, 0x53, +0x18, 0x56, 0x9E, 0x81, 0x08, 0x40, 0x83, 0x52, 0xA6, 0x00, 0x83, 0x96, 0x21, 0xC8, 0x03, 0x59, +0xE0, 0xA9, 0x20, 0x88, 0x83, 0x52, 0x88, 0x80, 0x26, 0x08, 0x80, 0x38, 0x86, 0xC0, 0x08, 0x40, +0x20, 0x88, 0x83, 0x52, 0x88, 0x80, 0x26, 0x08, 0x80, 0x38, 0x85, 0xC0, 0x08, 0x40, 0x95, 0x41, +0x96, 0x41, 0x97, 0x81, 0x98, 0x01, 0x99, 0x41, 0x9A, 0x41, 0x9B, 0x81, 0x9C, 0x41, 0x9E, 0x81, +0x19, 0x54, 0x19, 0x95, 0x18, 0x56, 0x8B, 0x41, 0xD4, 0x41, 0x02, 0xF0, 0x54, 0x02, 0x03, 0x18, +0x19, 0xAA, 0x54, 0x08, 0x51, 0x3E, 0x84, 0x80, 0x83, 0x93, 0x80, 0x81, 0x54, 0x08, 0x5D, 0xBE, +0x84, 0x80, 0x07, 0x70, 0x80, 0x40, 0x54, 0x08, 0x32, 0x62, 0x8A, 0x51, 0x54, 0x08, 0x5D, 0xBE, +0x84, 0x80, 0x00, 0xCE, 0xF0, 0x39, 0x96, 0x00, 0x54, 0x9C, 0x12, 0x6A, 0x83, 0x52, 0x03, 0x53, +0x18, 0x14, 0x15, 0xAA, 0x83, 0x52, 0x03, 0x53, 0x18, 0xD0, 0x18, 0x55, 0x18, 0x11, 0xD4, 0x8A, +0xF5, 0x69, 0xD3, 0x81, 0x08, 0xF0, 0x53, 0x42, 0x03, 0x18, 0x2F, 0x2A, 0x53, 0x48, 0x55, 0x7E, +0x84, 0x80, 0x04, 0xF0, 0x83, 0x93, 0x80, 0x40, 0x53, 0x48, 0x55, 0x7E, 0x84, 0x80, 0x00, 0x48, +0x96, 0x00, 0x53, 0x48, 0x95, 0x00, 0x98, 0x54, 0x98, 0x10, 0xD3, 0xCA, 0x1A, 0xAA, 0x10, 0xF0, +0x9B, 0x40, 0x08, 0x40, 0xB5, 0x40, 0x51, 0x3E, 0x84, 0x80, 0x00, 0x48, 0xB4, 0x00, 0x23, 0x3E, +0x84, 0x80, 0x8A, 0x51, 0x1D, 0xE5, 0x8A, 0x51, 0xB1, 0x00, 0x05, 0x30, 0x03, 0xD0, 0xB1, 0xCD, +0xFF, 0x7E, 0x03, 0x9D, 0x3E, 0x2A, 0x34, 0x08, 0x2D, 0x7E, 0x84, 0x80, 0x8A, 0x51, 0x1D, 0xE5, +0x8A, 0x51, 0xB2, 0x00, 0x32, 0x87, 0x31, 0x04, 0x01, 0x38, 0xB3, 0x40, 0x35, 0x48, 0x22, 0xFE, +0x84, 0x80, 0x33, 0x48, 0x80, 0x40, 0x35, 0x48, 0x22, 0xFE, 0x84, 0x80, 0x00, 0x48, 0x83, 0x96, +0xA2, 0xC0, 0x83, 0x52, 0x35, 0x48, 0x83, 0x96, 0xA3, 0x00, 0x0C, 0x30, 0x92, 0xEB, 0x18, 0x12, +0x80, 0xF0, 0x9B, 0x40, 0x10, 0xF0, 0x9E, 0x40, 0x13, 0x1F, 0x6A, 0xEA, 0x83, 0x52, 0x03, 0x53, +0x18, 0x14, 0x6D, 0x2A, 0x83, 0x52, 0x03, 0x53, 0x18, 0xD0, 0x92, 0x1F, 0x73, 0x2A, 0x83, 0x96, +0x03, 0x53, 0xA4, 0x54, 0x76, 0x2A, 0x83, 0x96, 0x03, 0x53, 0xA4, 0x10, 0x83, 0x52, 0x12, 0xDF, +0x7D, 0x6A, 0x83, 0x96, 0x03, 0x53, 0x24, 0x14, 0x80, 0x2A, 0x83, 0x96, 0x03, 0x53, 0x24, 0xD0, +0x83, 0x52, 0x5F, 0xC8, 0xB1, 0x00, 0x06, 0x30, 0x03, 0xD0, 0xB1, 0xCD, 0xFF, 0x7E, 0x03, 0x9D, +0x84, 0x6A, 0x12, 0xC8, 0x3F, 0xB9, 0x31, 0x04, 0xA5, 0x00, 0x83, 0x96, 0x24, 0x5C, 0x92, 0xAA, +0x83, 0x52, 0x9E, 0x15, 0x83, 0x52, 0x13, 0x08, 0x0F, 0x39, 0x3F, 0xFE, 0x84, 0x80, 0x83, 0x93, +0x00, 0x48, 0xA1, 0xC0, 0x21, 0xC8, 0xB3, 0x40, 0x3F, 0x30, 0xB3, 0xC5, 0x33, 0x48, 0x83, 0x96, +0xA2, 0xC0, 0x00, 0xB0, 0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, 0xA3, 0x00, 0x08, 0xF0, +0x92, 0xA3, 0x8A, 0x51, 0x21, 0xDF, 0xB0, 0xAA, 0x83, 0x52, 0x03, 0x53, 0x19, 0x96, 0xB3, 0x2A, +0x83, 0x52, 0x03, 0x53, 0x19, 0x52, 0xA1, 0x1F, 0xB9, 0x2A, 0x83, 0x52, 0x03, 0x53, 0x99, 0xD6, +0xBC, 0x2A, 0x83, 0x52, 0x03, 0x53, 0x99, 0x92, 0x83, 0x96, 0xA4, 0x9C, 0xCC, 0xEA, 0x6C, 0xB0, +0xA2, 0xC0, 0x00, 0xB0, 0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, 0xA3, 0x00, 0x0A, 0x30, +0x92, 0xA3, 0x8A, 0x51, 0x04, 0xF0, 0xD8, 0xEA, 0x60, 0x30, 0xA2, 0xC0, 0x00, 0xB0, 0x83, 0x52, +0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, 0xA3, 0x00, 0x0A, 0x30, 0x92, 0xA3, 0x8A, 0x51, 0x05, 0x30, +0x83, 0x96, 0xA2, 0xC0, 0x00, 0xB0, 0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, 0xA3, 0x00, +0x0B, 0x70, 0x92, 0xA3, 0x8A, 0x51, 0x25, 0x08, 0x83, 0x96, 0xA2, 0xC0, 0x00, 0xB0, 0x83, 0x52, +0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, 0xA3, 0x00, 0x04, 0xF0, 0x92, 0xA3, 0x8A, 0x51, 0x11, 0xC8, +0x83, 0x96, 0xA2, 0xC0, 0x00, 0xB0, 0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, 0xA3, 0x00, +0x05, 0x30, 0x92, 0xA3, 0x8A, 0x51, 0x10, 0x88, 0x83, 0x96, 0xA2, 0xC0, 0x00, 0xB0, 0x83, 0x52, +0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, 0xA3, 0x00, 0x06, 0x30, 0x92, 0xA3, 0x8A, 0x51, 0x83, 0x96, +0x00, 0xB0, 0xA2, 0x01, 0xA2, 0x4A, 0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, 0xA3, 0x00, +0x00, 0xB0, 0x92, 0xA3, 0x8A, 0x51, 0x35, 0xB0, 0xB1, 0x00, 0xB1, 0xCB, 0x15, 0xEB, 0x05, 0x30, +0x83, 0x96, 0x03, 0x53, 0xA2, 0xC0, 0x00, 0xB0, 0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, +0xA3, 0x00, 0x00, 0xB0, 0x92, 0xA3, 0x8A, 0x51, 0x03, 0x30, 0xB2, 0x00, 0x7D, 0x30, 0xB1, 0x00, +0xB1, 0xCB, 0x28, 0xAB, 0xB2, 0xCB, 0x28, 0xAB, 0x83, 0x52, 0x03, 0x53, 0x13, 0x1F, 0x32, 0xEB, +0x19, 0x51, 0x33, 0x2B, 0x19, 0x10, 0x05, 0x30, 0xB1, 0x00, 0xB1, 0xCB, 0x35, 0x2B, 0x83, 0x52, +0x03, 0x53, 0x13, 0x1F, 0x3D, 0x6B, 0x99, 0xD5, 0x3E, 0x6B, 0x99, 0x94, 0x15, 0x70, 0xB2, 0x00, +0xC6, 0xB0, 0xB1, 0x00, 0xB1, 0xCB, 0x42, 0xAB, 0xB2, 0xCB, 0x42, 0xAB, 0x00, 0x00, 0x0D, 0x70, +0x83, 0x96, 0x03, 0x53, 0xA2, 0xC0, 0x00, 0xB0, 0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, +0xA3, 0x00, 0x00, 0xB0, 0x92, 0xA3, 0x8A, 0x51, 0x93, 0x1B, 0x54, 0xEB, 0x99, 0x92, 0x19, 0x52, +0x83, 0x96, 0x00, 0xB0, 0xA2, 0x01, 0xA2, 0x4A, 0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, +0xA3, 0x00, 0x00, 0xB0, 0x92, 0xA3, 0x8A, 0x51, 0x13, 0x1F, 0x68, 0xEB, 0x99, 0x91, 0x69, 0x2B, +0x99, 0x50, 0xE4, 0xB0, 0xB1, 0x00, 0x6C, 0x2B, 0x6D, 0x6B, 0xB1, 0xCB, 0x6B, 0x6B, 0x70, 0xEB, +0x00, 0x00, 0x83, 0x52, 0x03, 0x53, 0x13, 0x1F, 0x77, 0xAB, 0x19, 0x95, 0x78, 0x2B, 0x19, 0x54, +0x03, 0x30, 0x83, 0x96, 0xA2, 0xC0, 0x00, 0xB0, 0x83, 0x52, 0x13, 0xDB, 0x01, 0xF0, 0x83, 0x96, +0xA3, 0x00, 0x00, 0xB0, 0x92, 0xA3, 0x8A, 0x51, 0x05, 0x30, 0xB2, 0x00, 0x26, 0x70, 0xB1, 0x00, +0xB1, 0xCB, 0x88, 0xAB, 0xB2, 0xCB, 0x88, 0xAB, 0x00, 0x00, 0x83, 0x52, 0x03, 0x53, 0x18, 0x56, +0x9E, 0x81, 0x08, 0x40, 0x83, 0x52, 0xB0, 0xC0, 0x83, 0x96, 0x23, 0x08, 0x03, 0x59, 0x9F, 0xAB, +0x22, 0xC8, 0x83, 0x52, 0x88, 0x80, 0x30, 0xC8, 0x80, 0x38, 0x86, 0xC0, 0x08, 0x40, 0x22, 0xC8, +0x83, 0x52, 0x88, 0x80, 0x30, 0xC8, 0x80, 0x38, 0x85, 0xC0, 0x08, 0x40, 0x11, 0x30, 0xBE, 0x80, +0x04, 0xF0, 0xA0, 0x80, 0x8A, 0x51, 0xE7, 0x21, 0x8A, 0x51, 0xBD, 0xC1, 0x10, 0xF0, 0x3D, 0x82, +0x03, 0x18, 0xDD, 0xAB, 0x3E, 0x88, 0x10, 0x7A, 0x03, 0x59, 0xBB, 0xAB, 0x8A, 0x51, 0x8A, 0xA5, +0x8A, 0x51, 0xBE, 0x80, 0xB2, 0x2B, 0x3D, 0x88, 0x20, 0x38, 0x9B, 0x40, 0x3E, 0x88, 0x10, 0x7A, +0x03, 0x9D, 0xC7, 0x6B, 0x8A, 0x51, 0x8A, 0xA5, 0x8A, 0x51, 0xBE, 0x80, 0xBE, 0xAB, 0x3E, 0x88, +0x11, 0xBA, 0x03, 0x9D, 0xCE, 0x6B, 0x30, 0x30, 0x9B, 0x40, 0xDD, 0xAB, 0x3D, 0x88, 0x3F, 0xFE, +0x84, 0x80, 0x3E, 0x88, 0x83, 0x93, 0x80, 0x40, 0x3D, 0x88, 0x30, 0x78, 0x9B, 0x40, 0xBD, 0x0A, +0xAE, 0x6B, 0x8A, 0x51, 0x8A, 0xA5, 0x8A, 0x51, 0xBE, 0x80, 0x3E, 0x88, 0x10, 0x7A, 0x03, 0x9D, +0xD9, 0x6B, 0x20, 0xF0, 0x9B, 0x40, 0x3E, 0x88, 0x10, 0x7A, 0x03, 0x9D, 0xEC, 0x6B, 0x8A, 0x51, +0x8A, 0xA5, 0x8A, 0x51, 0xBE, 0x80, 0xE3, 0x6B, 0x3E, 0x88, 0xDF, 0xC0, 0x5F, 0xC8, 0x30, 0x78, +0x9B, 0x40, 0x3E, 0x88, 0x10, 0x7A, 0x03, 0x59, 0xFA, 0xAB, 0x8A, 0x51, 0x8A, 0xA5, 0x8A, 0x51, +0xBE, 0x80, 0xF1, 0x6B, 0x20, 0xF0, 0x9B, 0x40, 0x3E, 0x88, 0x10, 0x7A, 0x03, 0x9D, 0x05, 0x6C, +0x8A, 0x51, 0x8A, 0xA5, 0x8A, 0x51, 0xBE, 0x80, 0xFC, 0xAB, 0x3E, 0x88, 0xBC, 0x40, 0x30, 0x78, +0x9B, 0x40, 0x3E, 0x88, 0x10, 0x7A, 0x03, 0x59, 0x12, 0x6C, 0x8A, 0x51, 0x8A, 0xA5, 0x8A, 0x51, +0xBE, 0x80, 0x09, 0x6C, 0x40, 0xF0, 0x9B, 0x40, 0x93, 0x5F, 0x1B, 0xEC, 0x8A, 0x51, 0x5F, 0x22, +0x8A, 0x51, 0x40, 0xF0, 0x9B, 0x40, 0xD3, 0x81, 0x08, 0xF0, 0x53, 0x42, 0x03, 0x18, 0x53, 0xEC, +0x53, 0x48, 0x95, 0x00, 0x85, 0x70, 0x0D, 0x02, 0x03, 0x5C, 0x33, 0xEC, 0x53, 0x48, 0x55, 0x7E, +0x84, 0x80, 0x0E, 0x70, 0x83, 0x93, 0x00, 0x42, 0x03, 0x18, 0x33, 0xEC, 0x53, 0x48, 0x55, 0x7E, +0x84, 0x80, 0x00, 0x8A, 0x44, 0x6C, 0x34, 0x70, 0x0D, 0x02, 0x03, 0x18, 0x51, 0xAC, 0x53, 0x48, +0x55, 0x7E, 0x84, 0x80, 0x05, 0x30, 0x83, 0x93, 0x00, 0x42, 0x03, 0x5C, 0x51, 0xAC, 0x53, 0x48, +0x55, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xFF, 0x7E, 0xB6, 0x40, 0x53, 0x48, 0x55, 0x7E, 0x84, 0x80, +0x36, 0x48, 0x80, 0x40, 0x53, 0x48, 0x55, 0x7E, 0x84, 0x80, 0x00, 0x48, 0x96, 0x00, 0x98, 0x54, +0x98, 0x10, 0xD3, 0xCA, 0x1C, 0xAC, 0xD4, 0x41, 0x02, 0xF0, 0x54, 0x02, 0x03, 0x18, 0xC7, 0x2C, +0x54, 0x9C, 0x5E, 0x2C, 0x83, 0x52, 0x03, 0x53, 0x18, 0x14, 0x61, 0xAC, 0x83, 0x52, 0x03, 0x53, +0x18, 0xD0, 0x54, 0x08, 0x51, 0x3E, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0x03, 0x9D, 0x6A, 0xEC, +0x64, 0x70, 0x6B, 0x2C, 0x73, 0xF0, 0xB7, 0x80, 0x0E, 0x08, 0x37, 0x82, 0x03, 0x18, 0x7C, 0x2C, +0x54, 0x08, 0x5D, 0xBE, 0x84, 0x80, 0x0B, 0x70, 0x00, 0x42, 0x03, 0x18, 0x7C, 0x2C, 0x54, 0x08, +0x5D, 0xBE, 0x84, 0x80, 0x00, 0x8A, 0x8C, 0xAC, 0x2D, 0xB0, 0x0E, 0x02, 0x03, 0x18, 0x9A, 0xEC, +0x54, 0x08, 0x5D, 0xBE, 0x84, 0x80, 0x08, 0xF0, 0x00, 0x42, 0x03, 0x5C, 0x9A, 0xEC, 0x54, 0x08, +0x5D, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xFF, 0x7E, 0xB6, 0x40, 0x54, 0x08, 0x5D, 0xBE, 0x84, 0x80, +0x36, 0x48, 0x80, 0x40, 0x54, 0x08, 0x5D, 0xBE, 0x84, 0x80, 0x00, 0xCE, 0xF0, 0x39, 0x96, 0x00, +0x18, 0x55, 0x18, 0x11, 0x24, 0x30, 0x0F, 0x42, 0x03, 0x5C, 0xAA, 0xEC, 0x54, 0x08, 0x51, 0x3E, +0x84, 0x80, 0x09, 0x30, 0x00, 0x42, 0x03, 0x18, 0xAA, 0xEC, 0x54, 0x08, 0x51, 0x3E, 0x84, 0x80, +0x00, 0x8A, 0xB9, 0x2C, 0x10, 0xF0, 0x0F, 0x42, 0x03, 0x18, 0xC5, 0xEC, 0x54, 0x08, 0x51, 0x3E, +0x84, 0x80, 0x80, 0x88, 0x03, 0x59, 0xC5, 0xEC, 0x54, 0x08, 0x51, 0x3E, 0x84, 0x80, 0x00, 0x48, +0xFF, 0x7E, 0xB6, 0x40, 0x54, 0x08, 0x51, 0x3E, 0x84, 0x80, 0x36, 0x48, 0x80, 0x40, 0x54, 0x08, +0x8A, 0x51, 0x32, 0x62, 0x8A, 0x51, 0x98, 0x95, 0x98, 0x51, 0xD4, 0x8A, 0x54, 0xAC, 0x51, 0x08, +0x19, 0x3E, 0x84, 0x80, 0x8A, 0x51, 0x1D, 0xE5, 0x8A, 0x51, 0xBA, 0x40, 0x06, 0x30, 0xE0, 0xC0, +0x5D, 0x88, 0xF9, 0xFE, 0x8A, 0x51, 0x99, 0xE5, 0x8A, 0x51, 0xBB, 0x80, 0x3A, 0xC7, 0x97, 0x40, +0x52, 0x08, 0x19, 0x3E, 0x84, 0x80, 0x8A, 0x51, 0x1D, 0xE5, 0x8A, 0x51, 0xBA, 0x40, 0x06, 0x30, +0xE0, 0xC0, 0x5E, 0x88, 0xF9, 0xFE, 0x8A, 0x51, 0x99, 0xE5, 0x8A, 0x51, 0xBB, 0x80, 0x3A, 0xC7, +0x9C, 0x00, 0x3C, 0x48, 0x95, 0x00, 0x0D, 0x08, 0xB6, 0x40, 0x03, 0xD0, 0xB6, 0xCC, 0x03, 0xD0, +0xB6, 0xCC, 0x03, 0xD0, 0xB6, 0xCC, 0x36, 0x48, 0x01, 0xBE, 0x84, 0x80, 0x8A, 0x51, 0x1D, 0xE5, +0x8A, 0x51, 0xB9, 0x40, 0x06, 0x30, 0xE0, 0xC0, 0x3C, 0x48, 0x55, 0x7E, 0x84, 0x80, 0x83, 0x93, +0x00, 0x48, 0xFC, 0xFE, 0x8A, 0x51, 0x99, 0xE5, 0x8A, 0x51, 0xB8, 0x00, 0x01, 0xF0, 0xB6, 0x40, +0x3C, 0x8A, 0x0C, 0xAD, 0x03, 0xD0, 0xB6, 0x0D, 0xFF, 0x7E, 0x03, 0x9D, 0x0A, 0xAD, 0x36, 0x48, +0x14, 0x05, 0x03, 0x59, 0x16, 0xED, 0x38, 0x08, 0x1C, 0x87, 0x18, 0xAD, 0x38, 0x08, 0x17, 0xC7, +0xB6, 0x40, 0x39, 0x48, 0x36, 0xC7, 0x9A, 0x00, 0x14, 0x6C, 0x05, 0x30, 0x8A, 0xC0, 0x04, 0x88, +0x84, 0x0A, 0x82, 0x47, 0x00, 0xF4, 0x00, 0xF4, 0x00, 0xF4, 0x00, 0xF4, 0x00, 0xF4, 0x00, 0xF4, +0x02, 0x34, 0x04, 0x34, 0x05, 0x74, 0x06, 0x74, 0x07, 0xB4, 0x08, 0x34, 0x09, 0x74, 0x0A, 0x74, +0x0A, 0x74, 0x0B, 0xB4, 0x0B, 0xB4, 0x0C, 0x74, 0x0D, 0xB4, 0x0D, 0xB4, 0x0E, 0xB4, 0x0E, 0xB4, +0x0E, 0xB4, 0x0F, 0xF4, 0x0F, 0xF4, 0x00, 0xF4, 0x06, 0x74, 0x0C, 0x74, 0x12, 0x74, 0x18, 0x74, +0x1E, 0xF4, 0x24, 0x74, 0x2A, 0xB4, 0x30, 0x74, 0x36, 0xF4, 0x01, 0x34, 0x01, 0x34, 0x01, 0x34, +0x02, 0x34, 0x03, 0x74, 0x04, 0x34, 0x05, 0x74, 0x05, 0x74, 0x06, 0x74, 0x06, 0x74, 0x0F, 0xF4, +0x0C, 0x74, 0x09, 0x74, 0x09, 0x74, 0x09, 0x74, 0x0C, 0x74, 0x0F, 0xF4, 0x0C, 0x74, 0x0F, 0xF4, +0x0C, 0x74, 0x83, 0x93, 0x51, 0x70, 0x84, 0x80, 0x60, 0x30, 0x8A, 0x51, 0xA7, 0x25, 0x8A, 0x51, +0x83, 0x96, 0xA4, 0x01, 0x83, 0x52, 0x38, 0x70, 0xBF, 0xC0, 0x3A, 0xB0, 0xC0, 0x80, 0x3C, 0xB0, +0xC1, 0xC0, 0x78, 0xB0, 0xC2, 0xC0, 0x7A, 0xF0, 0xC3, 0x00, 0x7C, 0xF0, 0xC4, 0xC0, 0x7D, 0x30, +0xC5, 0x00, 0x7F, 0x70, 0xC6, 0x00, 0xB9, 0xF0, 0xC7, 0x40, 0xBA, 0xF0, 0xC8, 0xC0, 0xBB, 0x30, +0xC9, 0x00, 0xFA, 0x30, 0xCA, 0x00, 0xFB, 0x70, 0xCB, 0x40, 0xFC, 0x30, 0xCC, 0x00, 0xFD, 0x70, +0xCD, 0x40, 0xFF, 0xB0, 0xCE, 0x40, 0x00, 0xB0, 0xCF, 0x80, 0x01, 0xF0, 0xD0, 0xC0, 0x83, 0x01, +0x8A, 0x51, 0xA6, 0x2B, 0x50, 0xC8, 0xB1, 0x00, 0x4F, 0x88, 0xB0, 0xC0, 0x31, 0x08, 0x30, 0x06, +0x03, 0x59, 0x97, 0x6D, 0x14, 0xC8, 0xB0, 0xC0, 0x14, 0xC8, 0xB1, 0x00, 0x8E, 0x2D, 0x31, 0x08, +0x08, 0x40, 0xB1, 0x00, 0xB0, 0x01, 0x60, 0xC8, 0x31, 0x58, 0xB0, 0x87, 0x03, 0xD0, 0xE0, 0x8D, +0x03, 0xD0, 0xB1, 0x8C, 0xB1, 0x48, 0x03, 0x9D, 0x9B, 0x6D, 0x30, 0xC8, 0x08, 0x40, 0x64, 0xC0, +0x80, 0x81, 0x84, 0x0A, 0x04, 0xC6, 0x03, 0x59, 0x00, 0xF4, 0x04, 0xC6, 0xA8, 0xED, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF +}; diff --git a/libloragw/src/arb_fw.var b/libloragw/src/arb_fw.var new file mode 100644 index 0000000..7f06906 --- /dev/null +++ b/libloragw/src/arb_fw.var @@ -0,0 +1,529 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Arbiter firmware + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Matthieu Leurent +*/ + +static uint8_t arb_firmware[MCU_ARB_FW_BYTE] = { +0x8A, 0x51, 0xAE, 0x6E, 0x00, 0xB0, 0x8A, 0xC0, 0x04, 0x88, 0x84, 0x0A, 0x82, 0x47, 0x00, 0xF4, +0x07, 0xB4, 0x06, 0x74, 0x05, 0x74, 0x04, 0x34, 0x03, 0x74, 0x02, 0x34, 0x01, 0x34, 0x00, 0xF4, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0x64, 0xC0, 0x80, 0x81, +0x84, 0x0A, 0x04, 0xC6, 0x03, 0x59, 0x00, 0xF4, 0x04, 0xC6, 0xA7, 0x6E, 0xD9, 0x81, 0x83, 0x93, +0x22, 0x30, 0x84, 0x80, 0x59, 0xB0, 0x8A, 0x51, 0xA6, 0xE6, 0x83, 0x01, 0x8A, 0x51, 0xB8, 0x2E, +0x01, 0xF0, 0xA0, 0x80, 0x8A, 0x51, 0x01, 0x67, 0x8A, 0x51, 0x0D, 0x58, 0xD4, 0x2E, 0xD3, 0x81, +0x59, 0x94, 0x53, 0x48, 0x8A, 0x51, 0xDB, 0x66, 0x8A, 0x51, 0x83, 0x52, 0x03, 0x53, 0xD9, 0x1C, +0xCC, 0x2E, 0x8A, 0x51, 0x85, 0xE7, 0x8A, 0x51, 0x0D, 0x58, 0xBD, 0xAE, 0x08, 0xF0, 0xD3, 0xCA, +0x53, 0x42, 0x03, 0x18, 0xBD, 0xAE, 0xC0, 0xAE, 0x59, 0xDC, 0xBD, 0xAE, 0x8A, 0x51, 0x32, 0xE7, +0x8A, 0x51, 0x59, 0x50, 0xBD, 0xAE, 0xDB, 0x80, 0xD9, 0x90, 0x5B, 0x88, 0x96, 0x00, 0x15, 0x70, +0xDA, 0x40, 0xDA, 0x0B, 0xE1, 0x2E, 0x83, 0x52, 0x03, 0x53, 0x8D, 0xDC, 0x08, 0x40, 0xD9, 0xD4, +0x15, 0x54, 0x15, 0x70, 0xDA, 0x40, 0xDA, 0x0B, 0xEB, 0xAE, 0x83, 0x52, 0x03, 0x53, 0x0D, 0xDD, +0xED, 0xAE, 0x10, 0x88, 0xD4, 0x00, 0x11, 0xC8, 0xD5, 0x40, 0x0F, 0x48, 0xD7, 0x80, 0x0E, 0x08, +0xD2, 0x00, 0x12, 0xC8, 0xD6, 0x40, 0x15, 0x10, 0x15, 0x70, 0xDA, 0x40, 0xDA, 0x0B, 0xFE, 0xEE, +0x08, 0x40, 0x95, 0x41, 0x96, 0x41, 0x97, 0x81, 0x98, 0x01, 0x99, 0x41, 0x9A, 0x41, 0x9B, 0x81, +0x9C, 0x41, 0x9E, 0x81, 0xD8, 0x41, 0x08, 0xF0, 0x58, 0x02, 0x03, 0x18, 0x29, 0x2F, 0x58, 0x08, +0x32, 0x3E, 0x84, 0x80, 0x80, 0x81, 0x58, 0x08, 0x3A, 0x7E, 0x84, 0x80, 0x80, 0x81, 0x58, 0x08, +0x4A, 0x3E, 0x84, 0x80, 0x80, 0x81, 0x58, 0x08, 0x22, 0xFE, 0x84, 0x80, 0x80, 0x81, 0x58, 0x08, +0x42, 0xFE, 0x84, 0x80, 0x80, 0x81, 0x58, 0x08, 0x2A, 0x3E, 0x84, 0x80, 0x80, 0x81, 0xD8, 0x8A, +0x0B, 0x2F, 0xD4, 0x41, 0xD5, 0x81, 0xD7, 0xC1, 0xD2, 0x41, 0xD6, 0x81, 0x59, 0x50, 0xD9, 0x90, +0xA1, 0x01, 0x08, 0x40, 0xD8, 0x41, 0x08, 0xF0, 0x58, 0x02, 0x03, 0x18, 0x08, 0x40, 0x58, 0x08, +0x2A, 0x3E, 0x84, 0x80, 0x83, 0x93, 0x00, 0xCB, 0x83, 0x2F, 0x58, 0x08, 0x2A, 0x3E, 0x84, 0x80, +0x80, 0x81, 0x58, 0x08, 0x42, 0xFE, 0x84, 0x80, 0x00, 0x48, 0x97, 0x40, 0x58, 0x08, 0x22, 0xFE, +0x84, 0x80, 0x00, 0x48, 0x98, 0xC0, 0x58, 0x08, 0x32, 0x3E, 0x84, 0x80, 0x00, 0x48, 0x99, 0x00, +0x58, 0x08, 0x3A, 0x7E, 0x84, 0x80, 0x00, 0x48, 0x9A, 0x00, 0x58, 0x08, 0x01, 0xBE, 0x84, 0x80, +0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x9B, 0x40, 0x58, 0x08, 0x4A, 0x3E, 0x84, 0x80, 0x00, 0x48, +0x9C, 0x00, 0x95, 0x94, 0x15, 0x70, 0xDA, 0x40, 0xDA, 0x0B, 0x64, 0x2F, 0x83, 0x52, 0x03, 0x53, +0x95, 0x50, 0x15, 0x70, 0xDA, 0x40, 0xDA, 0x0B, 0x6B, 0xAF, 0x01, 0xF0, 0x83, 0x52, 0x03, 0x53, +0xDA, 0x40, 0x58, 0x08, 0x01, 0xBE, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x01, 0xBE, +0x7B, 0xEF, 0x03, 0xD0, 0xDA, 0x0D, 0xFF, 0x7E, 0x03, 0x9D, 0x79, 0xAF, 0x5A, 0x48, 0x13, 0x45, +0x03, 0x59, 0x6D, 0xAF, 0xA1, 0x4A, 0xD8, 0x8A, 0x33, 0x6F, 0x59, 0x91, 0xD8, 0x41, 0x08, 0xF0, +0x58, 0x02, 0x03, 0x18, 0xBD, 0xEF, 0x0A, 0x30, 0x57, 0x82, 0x03, 0x18, 0x91, 0x2F, 0x59, 0xD5, +0xB9, 0xAF, 0x01, 0xF0, 0xDA, 0x40, 0x58, 0x08, 0x01, 0xBE, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, +0x8A, 0x51, 0x01, 0xBE, 0x9D, 0xAF, 0x03, 0xD0, 0xDA, 0x0D, 0xFF, 0x7E, 0x03, 0x9D, 0x9B, 0xAF, +0x5A, 0x48, 0x13, 0x45, 0x03, 0x9D, 0xAA, 0x6F, 0x58, 0x08, 0x2A, 0x3E, 0x84, 0x80, 0x83, 0x93, +0x00, 0xCB, 0xB9, 0xAF, 0x58, 0x08, 0x42, 0xFE, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0x56, 0x86, +0x03, 0x9D, 0xB9, 0xAF, 0x58, 0x08, 0x22, 0xFE, 0x84, 0x80, 0x00, 0x48, 0x52, 0x46, 0x03, 0x59, +0x8F, 0xAF, 0x59, 0xD9, 0xBD, 0xEF, 0xD8, 0x8A, 0x87, 0x6F, 0x59, 0xD9, 0x08, 0x40, 0xD8, 0x41, +0x08, 0xF0, 0x58, 0x02, 0x03, 0x18, 0x08, 0x40, 0x01, 0xF0, 0xDA, 0x40, 0x58, 0x08, 0x01, 0xBE, +0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x01, 0xBE, 0xD0, 0x2F, 0x03, 0xD0, 0xDA, 0x0D, +0xFF, 0x7E, 0x03, 0x9D, 0xCE, 0xAF, 0x5A, 0x48, 0x13, 0x45, 0x03, 0x9D, 0xFE, 0x2F, 0x58, 0x08, +0x2A, 0x3E, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0x03, 0x9D, 0xFE, 0x2F, 0x58, 0x08, 0x32, 0x3E, +0x84, 0x80, 0x54, 0x08, 0x80, 0x40, 0x58, 0x08, 0x3A, 0x7E, 0x84, 0x80, 0x55, 0x48, 0x80, 0x40, +0x58, 0x08, 0x4A, 0x3E, 0x84, 0x80, 0x57, 0x88, 0x80, 0x40, 0x58, 0x08, 0x22, 0xFE, 0x84, 0x80, +0x52, 0x08, 0x80, 0x40, 0x58, 0x08, 0x42, 0xFE, 0x84, 0x80, 0x56, 0x48, 0x80, 0x40, 0x58, 0x08, +0x2A, 0x3E, 0x84, 0x80, 0x01, 0xF0, 0x80, 0x40, 0x59, 0xD5, 0x08, 0x40, 0xD8, 0x8A, 0xC0, 0xEF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF +}; diff --git a/libloragw/src/cal_fw.var b/libloragw/src/cal_fw.var new file mode 100644 index 0000000..8c9389e --- /dev/null +++ b/libloragw/src/cal_fw.var @@ -0,0 +1,529 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Calibration firmware + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Matthieu Leurent +*/ + +static uint8_t cal_firmware[MCU_AGC_FW_BYTE] = { +0x8A, 0x51, 0x6F, 0x28, 0x00, 0xB0, 0x8A, 0xC0, 0x04, 0x88, 0x84, 0x0A, 0x82, 0x47, 0x00, 0xF4, +0x18, 0x74, 0x1C, 0xB4, 0x1E, 0xF4, 0x20, 0x34, 0x22, 0x74, 0x23, 0xB4, 0x24, 0x74, 0x25, 0xB4, +0x26, 0xB4, 0x27, 0xF4, 0x28, 0x74, 0x28, 0x74, 0x29, 0xB4, 0x2A, 0xB4, 0x2A, 0xB4, 0x2B, 0xF4, +0x2B, 0xF4, 0x2C, 0xB4, 0x2C, 0xB4, 0x2D, 0xF4, 0x2D, 0xF4, 0x2D, 0xF4, 0x2E, 0xF4, 0x2E, 0xF4, +0x2E, 0xF4, 0x2F, 0x34, 0x2F, 0x34, 0x2F, 0x34, 0x30, 0x74, 0x30, 0x74, 0x00, 0xF4, 0x00, 0xF4, +0x06, 0x74, 0x0A, 0x74, 0x0C, 0x74, 0x0E, 0xB4, 0x10, 0x34, 0x11, 0x74, 0x12, 0x74, 0x13, 0xB4, +0x14, 0x74, 0x15, 0xB4, 0x16, 0xB4, 0x16, 0xB4, 0x17, 0xF4, 0x18, 0x74, 0x0F, 0xF4, 0x0C, 0x74, +0x09, 0x74, 0x09, 0x74, 0x09, 0x74, 0x0C, 0x74, 0x0F, 0xF4, 0x0C, 0x74, 0x0F, 0xF4, 0x0C, 0x74, +0x01, 0x34, 0x01, 0x34, 0x01, 0x34, 0x02, 0x34, 0x03, 0x74, 0x04, 0x34, 0x05, 0x74, 0x05, 0x74, +0x06, 0x74, 0x06, 0x74, 0x02, 0x34, 0x02, 0x34, 0x03, 0x74, 0x04, 0x34, 0x05, 0x74, 0x06, 0x74, +0x07, 0xB4, 0x40, 0x34, 0x20, 0x34, 0x10, 0x34, 0x08, 0x34, 0x04, 0x34, 0x02, 0x34, 0x01, 0x34, +0x06, 0x74, 0x06, 0x74, 0x07, 0xB4, 0x07, 0xB4, 0x07, 0xB4, 0x10, 0x34, 0x08, 0x34, 0x04, 0x34, +0x02, 0x34, 0x01, 0x34, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0x64, 0xC0, +0x80, 0x81, 0x84, 0x0A, 0x04, 0xC6, 0x03, 0x59, 0x00, 0xF4, 0x04, 0xC6, 0x68, 0x68, 0xE6, 0x81, +0xE7, 0xC1, 0x83, 0x93, 0x55, 0xB0, 0x84, 0x80, 0x66, 0xB0, 0x8A, 0x51, 0x67, 0xA0, 0x8A, 0x51, +0xDA, 0xF0, 0x84, 0x80, 0xEE, 0x30, 0x8A, 0x51, 0x67, 0xA0, 0x8A, 0x51, 0x83, 0xD7, 0xA0, 0x30, +0x84, 0x80, 0xAE, 0xF0, 0x8A, 0x51, 0x67, 0xA0, 0x83, 0x96, 0x01, 0xF0, 0xEE, 0x80, 0x83, 0x52, +0x01, 0xF0, 0xE8, 0x00, 0x07, 0x70, 0xE9, 0x40, 0x06, 0x30, 0xEA, 0x40, 0x0F, 0xB0, 0xEB, 0x80, +0x01, 0xF0, 0xEC, 0x40, 0x05, 0x30, 0xED, 0x80, 0x02, 0xF0, 0xEE, 0x80, 0x0E, 0x70, 0xEF, 0xC0, +0x83, 0x01, 0x8A, 0x51, 0x4C, 0xAC, 0xA3, 0x41, 0x23, 0x08, 0xEA, 0xBE, 0x84, 0x80, 0x23, 0x08, +0xA1, 0xC0, 0x00, 0xB0, 0x22, 0x21, 0x8A, 0x51, 0x83, 0x93, 0x80, 0x40, 0x23, 0x08, 0xE0, 0x3E, +0x84, 0x80, 0x23, 0x08, 0xA1, 0xC0, 0x01, 0xF0, 0x22, 0x21, 0x8A, 0x51, 0x80, 0x40, 0x23, 0x08, +0xDE, 0xFE, 0x84, 0x80, 0x23, 0x08, 0xA1, 0xC0, 0x02, 0xF0, 0x22, 0x21, 0x8A, 0x51, 0x80, 0x40, +0x23, 0x08, 0xDA, 0xBE, 0x84, 0x80, 0x23, 0x08, 0xA1, 0xC0, 0x03, 0x30, 0x22, 0x21, 0x8A, 0x51, +0x80, 0x40, 0x23, 0x08, 0xE8, 0x7E, 0x84, 0x80, 0x23, 0x08, 0xA1, 0xC0, 0x04, 0xF0, 0x22, 0x21, +0x8A, 0x51, 0x80, 0x40, 0x23, 0x08, 0xE6, 0xBE, 0x84, 0x80, 0x23, 0x08, 0xA1, 0xC0, 0x05, 0x30, +0x22, 0x21, 0x8A, 0x51, 0x80, 0x40, 0x23, 0x08, 0xE4, 0x7E, 0x84, 0x80, 0x23, 0x08, 0xA1, 0xC0, +0x06, 0x30, 0x22, 0x21, 0x8A, 0x51, 0x80, 0x40, 0x23, 0x08, 0xAC, 0x7E, 0x84, 0x80, 0x23, 0x08, +0xA1, 0xC0, 0x08, 0xF0, 0x22, 0x21, 0x8A, 0x51, 0x83, 0xD7, 0x80, 0x40, 0x23, 0x08, 0xA8, 0x3E, +0x84, 0x80, 0x23, 0x08, 0xA1, 0xC0, 0x0A, 0x30, 0x22, 0x21, 0x8A, 0x51, 0x80, 0x40, 0x23, 0x08, +0xAA, 0x7E, 0x84, 0x80, 0x23, 0x08, 0xA1, 0xC0, 0x0B, 0x70, 0x22, 0x21, 0x8A, 0x51, 0x80, 0x40, +0x23, 0x08, 0xA2, 0x3E, 0x84, 0x80, 0x23, 0x08, 0xA1, 0xC0, 0x0C, 0x30, 0x22, 0x21, 0x8A, 0x51, +0x80, 0x40, 0x23, 0x08, 0xA4, 0x3E, 0x84, 0x80, 0x23, 0x08, 0xA1, 0xC0, 0x0D, 0x70, 0x22, 0x21, +0x8A, 0x51, 0x80, 0x40, 0x23, 0x08, 0xA6, 0x7E, 0x84, 0x80, 0x23, 0x08, 0xA1, 0xC0, 0x0E, 0x70, +0x22, 0x21, 0x8A, 0x51, 0x80, 0x40, 0x23, 0x08, 0xA0, 0xFE, 0x84, 0x80, 0x23, 0x08, 0xA1, 0xC0, +0x10, 0xF0, 0x22, 0x21, 0x8A, 0x51, 0x80, 0x40, 0x02, 0xF0, 0xA3, 0x8A, 0x23, 0x02, 0x03, 0x18, +0x08, 0x40, 0x9C, 0xA8, 0xA2, 0xC0, 0x21, 0xC8, 0x03, 0x59, 0x2B, 0xE9, 0x22, 0xC8, 0x86, 0xC0, +0x22, 0xC8, 0x86, 0xC0, 0x2F, 0x29, 0x22, 0xC8, 0x85, 0xC0, 0x22, 0xC8, 0x85, 0xC0, 0x08, 0x88, +0x08, 0x40, 0xB4, 0x00, 0x34, 0x8E, 0xF0, 0x39, 0x0C, 0x78, 0xB8, 0x00, 0xBB, 0xC1, 0x38, 0x08, +0xA1, 0xC0, 0x02, 0xF0, 0xA2, 0xC0, 0x54, 0x70, 0xA7, 0x23, 0x8A, 0x51, 0x38, 0x08, 0x02, 0xBE, +0xA1, 0xC0, 0x02, 0xF0, 0xA2, 0xC0, 0x54, 0x70, 0xA7, 0x23, 0x8A, 0x51, 0x02, 0xF0, 0xA1, 0xC0, +0x54, 0x70, 0x30, 0x24, 0x8A, 0x51, 0xE5, 0x40, 0xE5, 0x9F, 0x46, 0xA9, 0x02, 0xF0, 0xA1, 0xC0, +0x57, 0xF0, 0x30, 0x24, 0x8A, 0x51, 0xB5, 0x40, 0x02, 0xF0, 0xA1, 0xC0, 0x58, 0x70, 0x30, 0x24, +0x8A, 0x51, 0xB6, 0x40, 0x3B, 0x88, 0x25, 0x3E, 0x84, 0x80, 0x35, 0x48, 0x44, 0x24, 0x8A, 0x51, +0xA4, 0xC0, 0x36, 0x48, 0x44, 0x24, 0x8A, 0x51, 0x24, 0x47, 0x83, 0x93, 0x80, 0x40, 0x0F, 0xB0, +0xBB, 0x0A, 0x3B, 0x82, 0x03, 0x5C, 0x37, 0x29, 0xBA, 0x81, 0x25, 0x08, 0xB9, 0x40, 0xB7, 0xC1, +0xBB, 0xC1, 0x3B, 0x88, 0x25, 0x3E, 0x84, 0x80, 0x39, 0x48, 0x00, 0x42, 0x03, 0x18, 0x7F, 0xA9, +0x3B, 0x88, 0x25, 0x3E, 0x84, 0x80, 0x00, 0x48, 0xB9, 0x40, 0x3B, 0x88, 0xB7, 0x80, 0x0F, 0xB0, +0xBB, 0x0A, 0x3B, 0x82, 0x03, 0x5C, 0x71, 0xE9, 0x37, 0x88, 0x25, 0x3E, 0x84, 0x80, 0xFF, 0xB0, +0x80, 0x40, 0x08, 0xF0, 0xBA, 0xCA, 0x3A, 0x42, 0x03, 0x5C, 0x6D, 0x29, 0x39, 0x48, 0x08, 0x40, +0xB2, 0x00, 0xC9, 0x41, 0xCA, 0x41, 0xA1, 0xC0, 0x02, 0xF0, 0xA2, 0xC0, 0x53, 0xB0, 0xA7, 0x23, +0xC7, 0x81, 0x49, 0x08, 0xB3, 0x40, 0x4A, 0x08, 0xBC, 0x40, 0x47, 0x48, 0x4A, 0x3E, 0x84, 0x80, +0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x49, 0x87, 0xB4, 0x00, 0x47, 0x48, 0x4A, 0x3E, 0x84, 0x80, +0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x4A, 0x87, 0xBD, 0x80, 0x47, 0x48, 0x4A, 0x3E, 0x84, 0x80, +0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x49, 0x87, 0xB5, 0x40, 0x47, 0x48, 0x4A, 0x3E, 0x84, 0x80, +0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x4A, 0x02, 0xBE, 0x80, 0x47, 0x48, 0x4A, 0x3E, 0x84, 0x80, +0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x49, 0x02, 0xB6, 0x40, 0x47, 0x48, 0x4A, 0x3E, 0x84, 0x80, +0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x4A, 0x87, 0xBF, 0xC0, 0x47, 0x48, 0x4A, 0x3E, 0x84, 0x80, +0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x49, 0x02, 0xB7, 0x80, 0x47, 0x48, 0x4A, 0x3E, 0x84, 0x80, +0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x4A, 0x02, 0xC0, 0x80, 0xC8, 0x01, 0x48, 0xC8, 0x33, 0x7E, +0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0x27, 0xB0, 0xA2, 0x01, 0xA2, 0x4A, 0xA7, 0x23, 0x8A, 0x51, +0x48, 0xC8, 0x3C, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0x28, 0x30, 0xA2, 0x01, 0xA2, 0x4A, +0xA7, 0x23, 0x47, 0x48, 0x43, 0x3E, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0xD2, 0xE3, +0x8A, 0x51, 0xC6, 0x00, 0x48, 0xC8, 0x03, 0x59, 0x01, 0x2A, 0x45, 0x08, 0x46, 0x02, 0x03, 0x18, +0x0D, 0xAA, 0x46, 0x08, 0xC5, 0x00, 0x48, 0xC8, 0x33, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xC9, 0x00, +0x48, 0xC8, 0x3C, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xCA, 0x00, 0x05, 0x30, 0xC8, 0x4A, 0x48, 0xC2, +0x03, 0x5C, 0xDE, 0x69, 0x07, 0x70, 0xC7, 0xCA, 0x47, 0x42, 0x49, 0x08, 0xB3, 0x40, 0x4A, 0x08, +0xBC, 0x40, 0x03, 0x5C, 0x9D, 0x29, 0x49, 0x4A, 0xB4, 0x00, 0x4A, 0x4A, 0xBD, 0x80, 0x49, 0x4A, +0xB5, 0x40, 0x4A, 0x43, 0xBE, 0x80, 0x49, 0x43, 0xB6, 0x40, 0x4A, 0x4A, 0xBF, 0xC0, 0x49, 0x43, +0xB7, 0x80, 0x4A, 0x43, 0xC0, 0x80, 0x49, 0x08, 0xB8, 0x00, 0x4A, 0x4A, 0xC1, 0xC0, 0x49, 0x08, +0xB9, 0x40, 0x4A, 0x43, 0xC2, 0xC0, 0x49, 0x4A, 0xBA, 0x40, 0x4A, 0x08, 0xC3, 0x00, 0x49, 0x43, +0xBB, 0x80, 0x4A, 0x08, 0xC4, 0xC0, 0xC8, 0x01, 0x48, 0xC8, 0x33, 0x7E, 0x84, 0x80, 0x00, 0x48, +0xA1, 0xC0, 0x27, 0xB0, 0xA2, 0x01, 0xA2, 0x4A, 0xA7, 0x23, 0x8A, 0x51, 0x48, 0xC8, 0x3C, 0x7E, +0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0x28, 0x30, 0xA2, 0x01, 0xA2, 0x4A, 0xA7, 0x23, 0x8A, 0x51, +0x07, 0x70, 0xD2, 0xE3, 0x8A, 0x51, 0xC6, 0x00, 0x48, 0xC8, 0x03, 0x59, 0x5B, 0x2A, 0x45, 0x08, +0x46, 0x02, 0x03, 0x18, 0x67, 0x2A, 0x46, 0x08, 0xC5, 0x00, 0x48, 0xC8, 0x33, 0x7E, 0x84, 0x80, +0x00, 0x48, 0xC9, 0x00, 0x48, 0xC8, 0x3C, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xCA, 0x00, 0x09, 0x30, +0xC8, 0x4A, 0x48, 0xC2, 0x03, 0x5C, 0x3C, 0xEA, 0x49, 0x08, 0xA1, 0xC0, 0x27, 0xB0, 0xA2, 0x01, +0xA2, 0x4A, 0xA7, 0x23, 0x8A, 0x51, 0x4A, 0x08, 0xA1, 0xC0, 0x28, 0x30, 0xA2, 0x01, 0xA2, 0x4A, +0xA7, 0x6B, 0xB3, 0x40, 0xCB, 0x81, 0xCC, 0x41, 0x33, 0xCB, 0x82, 0x6A, 0x74, 0xB0, 0xC7, 0x40, +0x75, 0xF0, 0x85, 0xAA, 0x72, 0xB0, 0xC7, 0x40, 0x73, 0xF0, 0xC8, 0xC0, 0x33, 0x48, 0xA1, 0xC0, +0x02, 0xF0, 0xA2, 0xC0, 0x53, 0xB0, 0xA7, 0x23, 0xCA, 0x41, 0x4B, 0x48, 0xB5, 0x40, 0x4C, 0x08, +0xBE, 0x80, 0x4A, 0x08, 0x56, 0x7E, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x4B, 0xC7, +0xB6, 0x40, 0x4A, 0x08, 0x56, 0x7E, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x4C, 0x87, +0xBF, 0xC0, 0x4A, 0x08, 0x56, 0x7E, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x4B, 0xC7, +0xB7, 0x80, 0x4A, 0x08, 0x56, 0x7E, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x4C, 0x02, +0xC0, 0x80, 0x4A, 0x08, 0x56, 0x7E, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x4B, 0x42, +0xB8, 0x00, 0x4A, 0x08, 0x56, 0x7E, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x4C, 0x87, +0xC1, 0xC0, 0x4A, 0x08, 0x56, 0x7E, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x4B, 0x42, +0xB9, 0x40, 0x4A, 0x08, 0x56, 0x7E, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x51, 0x4C, 0x02, +0xC2, 0xC0, 0xCD, 0x81, 0x1F, 0xF0, 0xA1, 0xC0, 0xE0, 0x70, 0xA2, 0xC0, 0x4D, 0x48, 0x35, 0x7E, +0x84, 0x80, 0x00, 0x48, 0xBB, 0x63, 0x8A, 0x51, 0xB2, 0x00, 0x4D, 0x48, 0x35, 0x7E, 0x84, 0x80, +0x32, 0x08, 0x80, 0x40, 0x1F, 0xF0, 0xA1, 0xC0, 0xE0, 0x70, 0xA2, 0xC0, 0x4D, 0x48, 0x3E, 0xBE, +0x84, 0x80, 0x00, 0x48, 0xBB, 0x63, 0x8A, 0x51, 0xB2, 0x00, 0x4D, 0x48, 0x3E, 0xBE, 0x84, 0x80, +0x32, 0x08, 0x80, 0x40, 0x4D, 0x48, 0x35, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0xA2, 0x01, +0x47, 0x48, 0xA7, 0x23, 0x8A, 0x51, 0x4D, 0x48, 0x3E, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, +0xA2, 0x01, 0x48, 0xC8, 0xA7, 0x23, 0x4A, 0x08, 0x51, 0x3E, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, +0x8A, 0x51, 0xD2, 0xE3, 0x8A, 0x51, 0xC9, 0x00, 0x4D, 0x48, 0x03, 0x59, 0x13, 0xEB, 0x34, 0x08, +0x49, 0x02, 0x03, 0x18, 0x1F, 0x6B, 0x49, 0x08, 0xB4, 0x00, 0x4D, 0x48, 0x35, 0x7E, 0x84, 0x80, +0x00, 0x48, 0xCB, 0x40, 0x4D, 0x48, 0x3E, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xCC, 0x00, 0x05, 0x30, +0xCD, 0xCA, 0x4D, 0x42, 0x03, 0x5C, 0xD2, 0xEA, 0x05, 0x30, 0xCA, 0x8A, 0x4A, 0x02, 0x4B, 0x48, +0xB5, 0x40, 0x4C, 0x08, 0xBE, 0x80, 0x03, 0x5C, 0x91, 0xAA, 0x4B, 0x8A, 0xB6, 0x40, 0x4C, 0x4A, +0xBF, 0xC0, 0x4B, 0x8A, 0xB7, 0x80, 0x4C, 0x43, 0xC0, 0x80, 0x4B, 0x83, 0xB8, 0x00, 0x4C, 0x4A, +0xC1, 0xC0, 0x4B, 0x83, 0xB9, 0x40, 0x4C, 0x43, 0xC2, 0xC0, 0x4B, 0x48, 0xBA, 0x40, 0x4C, 0x4A, +0xC3, 0x00, 0x4B, 0x48, 0xBB, 0x80, 0x4C, 0x43, 0xC4, 0xC0, 0x4B, 0x8A, 0xBC, 0x40, 0x4C, 0x08, +0xC5, 0x00, 0x4B, 0x83, 0xBD, 0x80, 0x4C, 0x08, 0xC6, 0x00, 0xCD, 0x81, 0x1F, 0xF0, 0xA1, 0xC0, +0xE0, 0x70, 0xA2, 0xC0, 0x4D, 0x48, 0x35, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xBB, 0x63, 0x8A, 0x51, +0xB2, 0x00, 0x4D, 0x48, 0x35, 0x7E, 0x84, 0x80, 0x32, 0x08, 0x80, 0x40, 0x1F, 0xF0, 0xA1, 0xC0, +0xE0, 0x70, 0xA2, 0xC0, 0x4D, 0x48, 0x3E, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xBB, 0x63, 0x8A, 0x51, +0xB2, 0x00, 0x4D, 0x48, 0x3E, 0xBE, 0x84, 0x80, 0x32, 0x08, 0x80, 0x40, 0x4D, 0x48, 0x35, 0x7E, +0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0xA2, 0x01, 0x47, 0x48, 0xA7, 0x23, 0x8A, 0x51, 0x4D, 0x48, +0x3E, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0xA2, 0x01, 0x48, 0xC8, 0xA7, 0x23, 0x8A, 0x51, +0x07, 0x70, 0xD2, 0xE3, 0x8A, 0x51, 0xC9, 0x00, 0x4D, 0x48, 0x03, 0x59, 0x8B, 0x2B, 0x34, 0x08, +0x49, 0x02, 0x03, 0x18, 0x97, 0x6B, 0x49, 0x08, 0xB4, 0x00, 0x4D, 0x48, 0x35, 0x7E, 0x84, 0x80, +0x00, 0x48, 0xCB, 0x40, 0x4D, 0x48, 0x3E, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xCC, 0x00, 0x09, 0x30, +0xCD, 0xCA, 0x4D, 0x42, 0x03, 0x5C, 0x4E, 0x2B, 0x4B, 0x48, 0xA1, 0xC0, 0xA2, 0x01, 0x47, 0x48, +0xA7, 0x23, 0x8A, 0x51, 0x4C, 0x08, 0xA1, 0xC0, 0xA2, 0x01, 0x48, 0xC8, 0xA7, 0x6B, 0xA3, 0x00, +0x22, 0x0A, 0x03, 0x59, 0xB5, 0x6B, 0x22, 0xC8, 0x63, 0x86, 0x03, 0x59, 0xB5, 0x6B, 0x22, 0xC8, +0x88, 0x80, 0x80, 0xF0, 0x8C, 0xC0, 0x22, 0xC8, 0xE3, 0x40, 0x21, 0xC8, 0x88, 0x80, 0x23, 0x08, +0x80, 0x38, 0x8C, 0xC0, 0x08, 0x40, 0xA4, 0xC0, 0x21, 0xC8, 0x80, 0x7A, 0xA3, 0x00, 0x24, 0xC8, +0x80, 0x7A, 0xA3, 0x42, 0x03, 0x18, 0xC6, 0x2B, 0x21, 0xC8, 0x08, 0x40, 0x24, 0xC8, 0x80, 0x7A, +0xA3, 0x00, 0x22, 0xC8, 0x80, 0x7A, 0xA3, 0x42, 0x03, 0x18, 0xD0, 0xEB, 0x22, 0xC8, 0x08, 0x40, +0x24, 0xC8, 0x08, 0x40, 0xAA, 0x00, 0x2A, 0x8E, 0xF0, 0x39, 0x0C, 0x78, 0xAE, 0x40, 0xB1, 0x41, +0x2E, 0x48, 0xA1, 0xC0, 0x02, 0xF0, 0xA2, 0xC0, 0x54, 0x70, 0xA7, 0x23, 0x8A, 0x51, 0x2E, 0x48, +0x02, 0xBE, 0xA1, 0xC0, 0x02, 0xF0, 0xA2, 0xC0, 0x54, 0x70, 0xA7, 0x23, 0x8A, 0x51, 0x02, 0xF0, +0xA1, 0xC0, 0x54, 0x70, 0x30, 0x24, 0x8A, 0x51, 0xE5, 0x40, 0xE5, 0x9F, 0xE7, 0xAB, 0x02, 0xF0, +0xA1, 0xC0, 0x57, 0xF0, 0x30, 0x24, 0x8A, 0x51, 0xAB, 0x40, 0x02, 0xF0, 0xA1, 0xC0, 0x58, 0x70, +0x30, 0x24, 0x8A, 0x51, 0xAC, 0x00, 0x31, 0x08, 0x25, 0x3E, 0x84, 0x80, 0x2B, 0x48, 0x44, 0x24, +0x8A, 0x51, 0xA4, 0xC0, 0x2C, 0x08, 0x44, 0x24, 0x8A, 0x51, 0x24, 0x47, 0x80, 0x40, 0x05, 0x30, +0xB1, 0x8A, 0x31, 0x02, 0x03, 0x5C, 0xD8, 0x2B, 0xB0, 0x01, 0x25, 0x08, 0xAF, 0x80, 0xAD, 0x81, +0xB1, 0x41, 0x31, 0x08, 0x25, 0x3E, 0x84, 0x80, 0x2F, 0x88, 0x00, 0x42, 0x03, 0x18, 0x1F, 0x2C, +0x31, 0x08, 0x25, 0x3E, 0x84, 0x80, 0x00, 0x48, 0xAF, 0x80, 0x31, 0x08, 0xAD, 0x40, 0x05, 0x30, +0xB1, 0x8A, 0x31, 0x02, 0x03, 0x5C, 0x11, 0x6C, 0x2D, 0x48, 0x25, 0x3E, 0x84, 0x80, 0xFF, 0xB0, +0x80, 0x40, 0x03, 0x30, 0xB0, 0x4A, 0x30, 0xC2, 0x03, 0x5C, 0x0D, 0xAC, 0x2F, 0x88, 0x08, 0x40, +0xA2, 0xC0, 0x21, 0x0A, 0x03, 0x59, 0x3E, 0x2C, 0x21, 0xC8, 0x63, 0x86, 0x03, 0x59, 0x3E, 0x2C, +0x21, 0xC8, 0x88, 0x80, 0x80, 0xF0, 0x8C, 0xC0, 0x21, 0xC8, 0xE3, 0x40, 0x22, 0xC8, 0x8C, 0xC0, +0x22, 0xC8, 0x8C, 0xC0, 0x08, 0x88, 0x08, 0x40, 0xA1, 0xC0, 0xA1, 0x1F, 0x4A, 0xAC, 0x21, 0x03, +0xFF, 0x3A, 0x08, 0x40, 0x21, 0xC8, 0x08, 0x40, 0x02, 0xF0, 0xA0, 0x80, 0x95, 0x41, 0x96, 0x41, +0x97, 0x81, 0x98, 0x01, 0x99, 0x41, 0x9A, 0x41, 0x9B, 0x81, 0x9C, 0x41, 0x10, 0xF0, 0x9E, 0x40, +0x8B, 0x41, 0x83, 0x96, 0xD0, 0x01, 0xD1, 0x41, 0x83, 0x52, 0xD4, 0x41, 0x54, 0x08, 0xA0, 0xFE, +0x84, 0x80, 0x80, 0x81, 0x54, 0x08, 0xA8, 0x3E, 0x84, 0x80, 0x80, 0x81, 0x54, 0x08, 0xB0, 0x3E, +0x84, 0x80, 0x80, 0x81, 0x54, 0x08, 0xB8, 0x7E, 0x84, 0x80, 0x80, 0x81, 0x54, 0x08, 0xC0, 0xFE, +0x84, 0x80, 0x80, 0x81, 0x54, 0x08, 0xC8, 0x3E, 0x84, 0x80, 0x80, 0x81, 0x54, 0x08, 0xD2, 0x7E, +0x84, 0x80, 0x08, 0xF0, 0x80, 0x81, 0xD4, 0x8A, 0x54, 0x02, 0x03, 0x5C, 0x5E, 0x2C, 0x00, 0xB0, +0xA1, 0x01, 0xA1, 0x43, 0x8A, 0x51, 0x30, 0x24, 0x8A, 0x51, 0x03, 0xBA, 0x03, 0x9D, 0x7F, 0xAC, +0x03, 0x30, 0xE3, 0x40, 0xC8, 0x70, 0xA1, 0xC0, 0x02, 0xF0, 0xA2, 0xC0, 0x56, 0xB0, 0x8A, 0x51, +0xA7, 0x23, 0x8A, 0x51, 0x02, 0xF0, 0xA1, 0xC0, 0x56, 0xB0, 0x8A, 0x51, 0x30, 0x24, 0x8A, 0x51, +0xE5, 0x40, 0xC8, 0xFA, 0x03, 0x59, 0x1B, 0x94, 0xC9, 0xB0, 0xA1, 0xC0, 0x04, 0xF0, 0xA2, 0x01, +0x8A, 0x95, 0xF0, 0x27, 0x8A, 0x51, 0x04, 0xF0, 0xA1, 0x01, 0x8A, 0x51, 0x22, 0x21, 0x8A, 0x51, +0xE5, 0x40, 0xC9, 0x3A, 0x03, 0x59, 0x9B, 0xD4, 0xCA, 0xB0, 0xA1, 0xC0, 0x04, 0xF0, 0xA2, 0x01, +0xA2, 0x4A, 0x8A, 0x95, 0xF0, 0x27, 0x8A, 0x51, 0x04, 0xF0, 0xA1, 0x01, 0xA1, 0x4A, 0x8A, 0x51, +0x22, 0x21, 0x8A, 0x51, 0xE5, 0x40, 0xCA, 0x3A, 0x03, 0x59, 0x1B, 0xD5, 0x14, 0xC8, 0xCE, 0x40, +0x06, 0x30, 0x03, 0xD0, 0xCE, 0xCC, 0xFF, 0x7E, 0x03, 0x9D, 0xC1, 0xAC, 0x4E, 0x48, 0xD1, 0x00, +0x14, 0x5C, 0xCE, 0x2C, 0x83, 0x52, 0x03, 0x53, 0x66, 0xD6, 0xD1, 0xEC, 0x83, 0x52, 0x03, 0x53, +0x66, 0x92, 0x94, 0x9C, 0xD7, 0x6C, 0x83, 0x52, 0x03, 0x53, 0xE6, 0x16, 0xDA, 0x2C, 0x83, 0x52, +0x03, 0x53, 0xE6, 0xD2, 0x14, 0x9D, 0xE0, 0xAC, 0x83, 0x52, 0x03, 0x53, 0xE6, 0x57, 0xE3, 0x2C, +0x83, 0x52, 0x03, 0x53, 0xE6, 0x13, 0x94, 0xDD, 0xE9, 0x2C, 0x83, 0x52, 0x03, 0x53, 0x67, 0xD4, +0xEC, 0x2C, 0x83, 0x52, 0x03, 0x53, 0x67, 0x90, 0x14, 0x9E, 0xF2, 0x2C, 0x83, 0x52, 0x03, 0x53, +0x66, 0x94, 0xF5, 0x6C, 0x83, 0x52, 0x03, 0x53, 0x66, 0x50, 0x94, 0xDE, 0xFB, 0xAC, 0x83, 0x52, +0x03, 0x53, 0x66, 0x17, 0xFE, 0xAC, 0x83, 0x52, 0x03, 0x53, 0x66, 0xD3, 0xD1, 0x48, 0x03, 0x9D, +0x05, 0xAD, 0x83, 0x52, 0x03, 0x53, 0xE6, 0x15, 0x08, 0x6D, 0x83, 0x52, 0x03, 0x53, 0xE6, 0xD1, +0x51, 0x8B, 0x0E, 0xED, 0x83, 0x52, 0x03, 0x53, 0xE6, 0xD4, 0x11, 0xAD, 0x83, 0x52, 0x03, 0x53, +0xE6, 0x90, 0x51, 0x08, 0x02, 0x7A, 0x03, 0x9D, 0x19, 0xED, 0x83, 0x52, 0x03, 0x53, 0x66, 0xD5, +0x1C, 0xED, 0x83, 0x52, 0x03, 0x53, 0x66, 0x91, 0x8A, 0x51, 0x9B, 0xA0, 0x8A, 0x51, 0xD4, 0x41, +0x54, 0x08, 0xE0, 0x3E, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0xCE, 0x40, 0x54, 0x08, 0xE2, 0x7E, +0x84, 0x80, 0x4E, 0x48, 0x80, 0x40, 0x54, 0x08, 0xDE, 0xFE, 0x84, 0x80, 0x00, 0x48, 0xCE, 0x40, +0x54, 0x08, 0x55, 0x7E, 0x84, 0x80, 0x4E, 0x48, 0x80, 0x40, 0x54, 0x08, 0xDA, 0xBE, 0x84, 0x80, +0x00, 0x48, 0xCE, 0x40, 0x54, 0x08, 0xDC, 0xBE, 0x84, 0x80, 0x4E, 0x48, 0x80, 0x40, 0x54, 0x08, +0xE8, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xCE, 0x40, 0x54, 0x08, 0x5B, 0xBE, 0x84, 0x80, 0x4E, 0x48, +0x80, 0x40, 0x54, 0x08, 0xE6, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xCE, 0x40, 0x54, 0x08, 0x59, 0x7E, +0x84, 0x80, 0x4E, 0x48, 0x80, 0x40, 0x54, 0x08, 0xE4, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xCE, 0x40, +0x54, 0x08, 0x57, 0xBE, 0x84, 0x80, 0x4E, 0x48, 0x80, 0x40, 0x02, 0xF0, 0xD4, 0x8A, 0x54, 0x02, +0x03, 0x5C, 0x20, 0x6D, 0x69, 0xB0, 0xA1, 0x01, 0xA2, 0x01, 0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, +0x19, 0x10, 0x19, 0x51, 0x99, 0x50, 0x99, 0x91, 0x21, 0x30, 0xA1, 0x01, 0xA1, 0x4A, 0xA2, 0x01, +0xA2, 0x4A, 0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, 0x21, 0x30, 0xA1, 0x01, 0xA2, 0x01, 0xA2, 0x4A, +0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, 0x07, 0x70, 0xA1, 0xC0, 0x02, 0xF0, 0xA2, 0xC0, 0x30, 0x30, +0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, 0xE6, 0xD8, 0xE8, 0x41, 0x27, 0xB0, 0xA1, 0x01, 0xA2, 0x01, +0xA2, 0x4A, 0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, 0x28, 0x30, 0xA1, 0x01, 0xA2, 0x01, 0xA2, 0x4A, +0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, 0x66, 0x1E, 0x3E, 0x6E, 0xE6, 0x5D, 0x66, 0xD9, 0x9A, 0x2D, +0xE6, 0x1C, 0xBD, 0xAD, 0x18, 0x14, 0xE6, 0x1C, 0xB2, 0x2D, 0x03, 0x30, 0xA1, 0xC0, 0x02, 0xF0, +0xA2, 0xC0, 0x2C, 0x70, 0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, 0x99, 0xD5, 0x29, 0x70, 0xD0, 0xC0, +0x96, 0xB0, 0xCF, 0x80, 0xA6, 0xB0, 0xCE, 0x40, 0xCE, 0x0B, 0xAC, 0x2D, 0xCF, 0x4B, 0xAC, 0x2D, +0xD0, 0x8B, 0xAC, 0x2D, 0x03, 0x30, 0x83, 0x52, 0x03, 0x53, 0xEE, 0x80, 0x0F, 0xB0, 0xEF, 0xC0, +0x00, 0xB0, 0x8A, 0x95, 0x02, 0x26, 0x8A, 0x51, 0xC7, 0x6D, 0x18, 0xD0, 0x02, 0xF0, 0xEE, 0x80, +0x0E, 0x70, 0xEF, 0xC0, 0x01, 0xF0, 0xAA, 0x41, 0x8A, 0x95, 0x75, 0x25, 0x8A, 0x51, 0x6C, 0x48, +0x83, 0x96, 0xD2, 0x00, 0x83, 0x52, 0x6B, 0x88, 0x83, 0x96, 0xD3, 0x40, 0x83, 0x52, 0x64, 0x08, +0x83, 0x96, 0xD4, 0x00, 0x83, 0x52, 0x02, 0xF0, 0xA1, 0x01, 0xA2, 0xC0, 0x53, 0xB0, 0x8A, 0x51, +0xA7, 0x23, 0x8A, 0x51, 0x14, 0x30, 0xA1, 0xC0, 0x02, 0xF0, 0xA2, 0xC0, 0x56, 0xB0, 0x8A, 0x51, +0xA7, 0x23, 0x8A, 0x51, 0x02, 0xF0, 0x8A, 0x51, 0x31, 0x61, 0x8A, 0x51, 0xD2, 0x00, 0xEC, 0xF0, +0xA1, 0xC0, 0x02, 0xF0, 0xA2, 0xC0, 0x56, 0xB0, 0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, 0x00, 0xB0, +0x8A, 0x51, 0x79, 0xE2, 0x8A, 0x51, 0x07, 0x70, 0x8A, 0x51, 0x31, 0x61, 0x8A, 0x51, 0xD3, 0x40, +0x02, 0xF0, 0xA1, 0x01, 0xA2, 0xC0, 0x2F, 0xF0, 0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, 0x24, 0x30, +0xA1, 0xC0, 0x02, 0xF0, 0xA2, 0xC0, 0x2F, 0xF0, 0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, 0x2A, 0x70, +0xCF, 0x80, 0x8D, 0xB0, 0xCE, 0x40, 0xCE, 0x0B, 0x0B, 0xEE, 0xCF, 0x4B, 0x0B, 0xEE, 0x10, 0x6E, +0x83, 0x52, 0x02, 0xF0, 0x03, 0x53, 0xA1, 0x01, 0xA2, 0xC0, 0x2F, 0xF0, 0x8A, 0x51, 0xA7, 0x23, +0x8A, 0x51, 0xE6, 0x5D, 0x66, 0xD9, 0x1E, 0x2E, 0xE6, 0x1C, 0x25, 0xEE, 0x99, 0x91, 0x03, 0x30, +0xA1, 0xC0, 0x00, 0xB0, 0xA2, 0x01, 0xA2, 0x4A, 0x29, 0xEE, 0x03, 0x30, 0xA1, 0xC0, 0x00, 0xB0, +0xA2, 0x01, 0x8A, 0x95, 0xF0, 0x27, 0x8A, 0x51, 0x52, 0x08, 0x8A, 0x95, 0xEB, 0x24, 0x8A, 0x51, +0xCE, 0x40, 0x53, 0x48, 0x8A, 0x95, 0xEB, 0x24, 0x8A, 0x51, 0x4E, 0x42, 0x1E, 0x7E, 0x83, 0x96, +0xD0, 0xC0, 0x32, 0x70, 0x50, 0xC2, 0x83, 0x52, 0x03, 0x18, 0x9B, 0x15, 0x83, 0x52, 0xE6, 0x5E, +0xEE, 0xAE, 0xE6, 0x5D, 0x66, 0xD9, 0x46, 0xEE, 0xE6, 0x1C, 0x69, 0x2E, 0x18, 0xD0, 0xE6, 0x1C, +0x5E, 0x6E, 0x03, 0x30, 0xA1, 0xC0, 0x02, 0xF0, 0xA2, 0xC0, 0x2C, 0x70, 0x8A, 0x51, 0xA7, 0x23, +0x8A, 0x51, 0x99, 0x94, 0x29, 0x70, 0xD0, 0xC0, 0x96, 0xB0, 0xCF, 0x80, 0xA6, 0xB0, 0xCE, 0x40, +0xCE, 0x0B, 0x58, 0xEE, 0xCF, 0x4B, 0x58, 0xEE, 0xD0, 0x8B, 0x58, 0xEE, 0x03, 0x30, 0x83, 0x52, +0x03, 0x53, 0xEE, 0x80, 0x0F, 0xB0, 0xEF, 0xC0, 0x01, 0xF0, 0x8A, 0x95, 0x02, 0x26, 0x8A, 0x51, +0x74, 0x2E, 0x18, 0x14, 0x02, 0xF0, 0xEE, 0x80, 0x0E, 0x70, 0xEF, 0xC0, 0x01, 0xF0, 0xAA, 0x41, +0xAA, 0x8A, 0x8A, 0x95, 0x75, 0x25, 0x8A, 0x51, 0x6C, 0x48, 0x83, 0x96, 0xD2, 0x00, 0x83, 0x52, +0x6B, 0x88, 0x83, 0x96, 0xD3, 0x40, 0x83, 0x52, 0x64, 0x08, 0x83, 0x96, 0xD4, 0x00, 0x83, 0x52, +0x02, 0xF0, 0xA1, 0x01, 0xA1, 0x4A, 0xA2, 0xC0, 0x53, 0xB0, 0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, +0x14, 0x30, 0xA1, 0xC0, 0x02, 0xF0, 0xA2, 0xC0, 0x56, 0xB0, 0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, +0x02, 0xF0, 0x8A, 0x51, 0x31, 0x61, 0x8A, 0x51, 0xD2, 0x00, 0xEC, 0xF0, 0xA1, 0xC0, 0x02, 0xF0, +0xA2, 0xC0, 0x56, 0xB0, 0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, 0x01, 0xF0, 0x8A, 0x51, 0x79, 0xE2, +0x8A, 0x51, 0x07, 0x70, 0x8A, 0x51, 0x31, 0x61, 0x8A, 0x51, 0xD3, 0x40, 0x02, 0xF0, 0xA1, 0x01, +0xA2, 0xC0, 0x2F, 0xF0, 0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, 0x25, 0x70, 0xA1, 0xC0, 0x02, 0xF0, +0xA2, 0xC0, 0x2F, 0xF0, 0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, 0x2A, 0x70, 0xCF, 0x80, 0x8D, 0xB0, +0xCE, 0x40, 0xCE, 0x0B, 0xB9, 0x6E, 0xCF, 0x4B, 0xB9, 0x6E, 0xBE, 0xAE, 0x83, 0x52, 0x02, 0xF0, +0x03, 0x53, 0xA1, 0x01, 0xA2, 0xC0, 0x2F, 0xF0, 0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, 0xE6, 0x5D, +0x66, 0xD9, 0xCC, 0x2E, 0xE6, 0x1C, 0xD4, 0x2E, 0xE6, 0x1C, 0xCF, 0xAE, 0x99, 0x50, 0x03, 0x30, +0xA1, 0xC0, 0x00, 0xB0, 0xA2, 0x01, 0xD9, 0x6E, 0x03, 0x30, 0xA1, 0xC0, 0x00, 0xB0, 0xA2, 0x01, +0xA2, 0x4A, 0x8A, 0x95, 0xF0, 0x27, 0x8A, 0x51, 0x52, 0x08, 0x8A, 0x95, 0xEB, 0x24, 0x8A, 0x51, +0xCE, 0x40, 0x53, 0x48, 0x8A, 0x95, 0xEB, 0x24, 0x8A, 0x51, 0x4E, 0x42, 0x1E, 0x7E, 0x83, 0x96, +0xD1, 0x00, 0x32, 0x70, 0x51, 0x02, 0x83, 0x52, 0x03, 0x18, 0x1B, 0xD6, 0x83, 0x52, 0x66, 0xDC, +0xF3, 0xAE, 0x03, 0x30, 0xF4, 0x6E, 0x02, 0xF0, 0xEE, 0x80, 0x01, 0xF0, 0xE1, 0x00, 0x14, 0x30, +0xA1, 0xC0, 0x02, 0xF0, 0xA2, 0xC0, 0x56, 0xB0, 0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, 0xE6, 0x9F, +0x72, 0x6F, 0x18, 0xD0, 0x02, 0xF0, 0xA1, 0x01, 0xA2, 0xC0, 0x53, 0xB0, 0x8A, 0x51, 0xA7, 0x23, +0x8A, 0x51, 0x9B, 0x16, 0xD4, 0x41, 0x27, 0xB0, 0xA1, 0x01, 0xA2, 0x01, 0xA2, 0x4A, 0x8A, 0x51, +0xA7, 0x23, 0x8A, 0x51, 0x28, 0x30, 0xA1, 0x01, 0xA2, 0x01, 0xA2, 0x4A, 0x8A, 0x51, 0xA7, 0x23, +0x8A, 0x51, 0x54, 0x08, 0x08, 0xBE, 0xEF, 0xC0, 0x00, 0xB0, 0xAA, 0x41, 0xD4, 0x48, 0x03, 0x59, +0x01, 0xF0, 0x8A, 0x95, 0x75, 0x25, 0x8A, 0x51, 0x54, 0x08, 0xD2, 0x7E, 0x84, 0x80, 0x6B, 0x0E, +0xF0, 0x39, 0x64, 0x04, 0x83, 0x93, 0x80, 0x40, 0x03, 0x30, 0x8A, 0x51, 0x31, 0x61, 0x8A, 0x51, +0xD2, 0x00, 0x00, 0xB0, 0x8A, 0x51, 0x90, 0x21, 0x8A, 0x51, 0x07, 0x70, 0x8A, 0x51, 0x31, 0x61, +0x8A, 0x51, 0xD3, 0x40, 0x52, 0x08, 0x8A, 0x95, 0xEB, 0x24, 0x8A, 0x51, 0xCE, 0x40, 0x53, 0x48, +0x8A, 0x95, 0xEB, 0x24, 0x8A, 0x51, 0x4E, 0x42, 0x18, 0xFE, 0xCF, 0x80, 0x54, 0x08, 0xC0, 0xFE, +0x84, 0x80, 0x4F, 0x88, 0x80, 0x40, 0x54, 0x08, 0xA0, 0xFE, 0x84, 0x80, 0x27, 0xB0, 0xA1, 0x01, +0xA1, 0x4A, 0x8A, 0x51, 0x30, 0x24, 0x8A, 0x51, 0x80, 0x40, 0x54, 0x08, 0xA8, 0x3E, 0x84, 0x80, +0x28, 0x30, 0xA1, 0x01, 0xA1, 0x4A, 0x8A, 0x51, 0x30, 0x24, 0x8A, 0x51, 0x80, 0x40, 0x54, 0x08, +0xC0, 0xFE, 0x84, 0x80, 0x14, 0x30, 0x00, 0x42, 0x03, 0x5C, 0x9B, 0xD2, 0x08, 0xF0, 0xD4, 0x8A, +0x54, 0x02, 0x03, 0x5C, 0x0B, 0x2F, 0x03, 0x30, 0xA1, 0xC0, 0x00, 0xB0, 0xA2, 0x01, 0x8A, 0x95, +0xF0, 0x27, 0x8A, 0x51, 0x67, 0x1C, 0xE8, 0x6F, 0x18, 0x14, 0x02, 0xF0, 0xA1, 0x01, 0xA1, 0x4A, +0xA2, 0xC0, 0x53, 0xB0, 0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, 0x1B, 0x17, 0xD4, 0x41, 0x27, 0xB0, +0xA1, 0x01, 0xA2, 0x01, 0xA2, 0x4A, 0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, 0x28, 0x30, 0xA1, 0x01, +0xA2, 0x01, 0xA2, 0x4A, 0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, 0x54, 0x08, 0x08, 0xBE, 0xEF, 0xC0, +0x00, 0xB0, 0xAA, 0x41, 0xAA, 0x8A, 0xD4, 0x48, 0x03, 0x59, 0x01, 0xF0, 0x8A, 0x95, 0x75, 0x25, +0x8A, 0x51, 0x54, 0x08, 0xD2, 0x7E, 0x84, 0x80, 0x6B, 0x0E, 0xF0, 0x39, 0x64, 0x04, 0x83, 0x93, +0x80, 0x40, 0x03, 0x30, 0x8A, 0x51, 0x31, 0x61, 0x8A, 0x51, 0xD2, 0x00, 0x01, 0xF0, 0x8A, 0x51, +0x90, 0x21, 0x8A, 0x51, 0x07, 0x70, 0x8A, 0x51, 0x31, 0x61, 0x8A, 0x51, 0xD3, 0x40, 0x52, 0x08, +0x8A, 0x95, 0xEB, 0x24, 0x8A, 0x51, 0xCE, 0x40, 0x53, 0x48, 0x8A, 0x95, 0xEB, 0x24, 0x8A, 0x51, +0x4E, 0x42, 0x18, 0xFE, 0xCF, 0x80, 0x54, 0x08, 0xC8, 0x3E, 0x84, 0x80, 0x4F, 0x88, 0x80, 0x40, +0x54, 0x08, 0xB0, 0x3E, 0x84, 0x80, 0x27, 0xB0, 0xA1, 0x01, 0xA1, 0x4A, 0x8A, 0x51, 0x30, 0x24, +0x8A, 0x51, 0x80, 0x40, 0x54, 0x08, 0xB8, 0x7E, 0x84, 0x80, 0x28, 0x30, 0xA1, 0x01, 0xA1, 0x4A, +0x8A, 0x51, 0x30, 0x24, 0x8A, 0x51, 0x80, 0x40, 0x54, 0x08, 0xC8, 0x3E, 0x84, 0x80, 0x14, 0x30, +0x00, 0x42, 0x03, 0x5C, 0x1B, 0xD3, 0x08, 0xF0, 0xD4, 0x8A, 0x54, 0x02, 0x03, 0x5C, 0x7F, 0x2F, +0x03, 0x30, 0xA1, 0xC0, 0x00, 0xB0, 0xA2, 0x01, 0xA2, 0x4A, 0x8A, 0x95, 0xF0, 0x27, 0x8A, 0x51, +0x18, 0x56, 0x18, 0x12, 0x8A, 0x95, 0x5B, 0x67, 0x8A, 0x51, 0xE4, 0xB0, 0xCE, 0x40, 0xF0, 0x6F, +0xF1, 0xAF, 0xCE, 0x0B, 0xEF, 0x2F, 0xF4, 0xAF, 0x00, 0x00, 0x0E, 0x70, 0x83, 0x52, 0x03, 0x53, +0xA1, 0xC0, 0x69, 0xB0, 0xA2, 0x01, 0x8A, 0x51, 0xA7, 0x23, 0x8A, 0x51, 0x9B, 0x57, 0xFF, 0x6F, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, +0xFF, 0xBF, 0xFF, 0xBF, 0xFF, 0xBF, 0xA2, 0xC0, 0x10, 0xF0, 0x22, 0xC2, 0x22, 0xC8, 0x03, 0x18, +0xF6, 0x6C, 0x1F, 0xBE, 0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x08, 0x40, 0xA1, 0xC0, 0x03, 0xD0, +0xA1, 0x4C, 0x03, 0xD0, 0xA1, 0x4C, 0x03, 0xD0, 0xA1, 0x4C, 0x21, 0xC8, 0x01, 0xBE, 0x84, 0x80, +0x8A, 0x51, 0x02, 0xA0, 0x08, 0x40, 0xA8, 0xC0, 0x28, 0x5C, 0x0A, 0xAD, 0x83, 0x52, 0x03, 0x53, +0x18, 0x14, 0x0D, 0xED, 0x83, 0x52, 0x03, 0x53, 0x18, 0xD0, 0x07, 0x70, 0xE4, 0x00, 0x64, 0x8E, +0xF0, 0x39, 0x96, 0x00, 0x18, 0x55, 0x18, 0x11, 0xA9, 0x41, 0x28, 0xC8, 0xA4, 0xC0, 0x00, 0xB0, +0x56, 0xE5, 0x8A, 0x95, 0x98, 0x95, 0x98, 0x51, 0xA7, 0x81, 0x74, 0xB0, 0x0E, 0x02, 0x03, 0x5C, +0x27, 0x2D, 0x0B, 0x70, 0x64, 0x02, 0x03, 0x18, 0x27, 0x2D, 0xE4, 0x8A, 0x31, 0xED, 0x2D, 0xB0, +0x0E, 0x02, 0x03, 0x18, 0x36, 0x2D, 0x08, 0xF0, 0x64, 0x02, 0x03, 0x5C, 0x36, 0x2D, 0xFF, 0xB0, +0xE4, 0xC7, 0x64, 0x8E, 0xF0, 0x39, 0x96, 0x00, 0x18, 0x55, 0x18, 0x11, 0x24, 0x30, 0x0F, 0x42, +0x03, 0x5C, 0x40, 0x6D, 0x09, 0x30, 0x29, 0x02, 0x03, 0x18, 0x40, 0x6D, 0xA9, 0x8A, 0x49, 0xED, +0x10, 0xF0, 0x0F, 0x42, 0x03, 0x18, 0x50, 0xAD, 0x29, 0x08, 0x03, 0x59, 0x50, 0xAD, 0xFF, 0xB0, +0xA9, 0xC7, 0x28, 0xC8, 0xA4, 0xC0, 0x29, 0x08, 0x56, 0xE5, 0x8A, 0x95, 0x98, 0x95, 0x98, 0x51, +0x14, 0x30, 0xA7, 0xCA, 0x27, 0x42, 0x03, 0x18, 0x08, 0x40, 0x1D, 0x2D, 0xA6, 0x00, 0x39, 0x7E, +0x84, 0x80, 0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x95, 0xEC, 0x40, 0x26, 0x08, 0x2F, 0xBE, 0x84, 0x80, +0x8A, 0x51, 0x02, 0xA0, 0x8A, 0x95, 0xEB, 0x80, 0x6C, 0x48, 0xA5, 0x00, 0x05, 0x30, 0x03, 0xD0, +0xA5, 0xCD, 0xFF, 0x7E, 0x03, 0xD0, 0x03, 0x9D, 0x68, 0xED, 0x6B, 0x0D, 0x25, 0x04, 0x68, 0x04, +0xA1, 0xC0, 0x24, 0xC8, 0xA2, 0xC0, 0x0C, 0x30, 0xF0, 0x6F, 0xAD, 0x40, 0x01, 0xF0, 0x83, 0x96, +0xED, 0x80, 0x83, 0x52, 0x2A, 0x08, 0xAA, 0xE6, 0x8A, 0x95, 0x2D, 0x48, 0x03, 0x59, 0xFE, 0xED, +0x2A, 0x08, 0xE2, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xAB, 0x40, 0x2A, 0x08, 0x5B, 0xBE, 0x84, 0x80, +0x2B, 0x48, 0x80, 0x40, 0x66, 0x5F, 0x92, 0xED, 0x2A, 0x08, 0x55, 0x7E, 0x84, 0x80, 0x00, 0x48, +0x28, 0xFE, 0x97, 0x6D, 0x2A, 0x08, 0x55, 0x7E, 0x84, 0x80, 0x00, 0x48, 0x14, 0xFE, 0xAB, 0x40, +0x2A, 0x08, 0x59, 0x7E, 0x84, 0x80, 0x2B, 0x48, 0x80, 0x40, 0x2A, 0x08, 0x55, 0x7E, 0x84, 0x80, +0x00, 0x48, 0xAB, 0x40, 0x2A, 0x08, 0x59, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xAC, 0x00, 0x2B, 0x48, +0x2C, 0x02, 0x2A, 0x08, 0x03, 0x18, 0xB6, 0x6D, 0x5B, 0xBE, 0x84, 0x80, 0x00, 0x8A, 0xAB, 0x40, +0x2A, 0x08, 0x5B, 0xBE, 0x84, 0x80, 0x2B, 0x48, 0x80, 0x40, 0x2A, 0x08, 0xDC, 0xBE, 0x84, 0x80, +0x00, 0x48, 0xAB, 0x40, 0x2A, 0x08, 0x57, 0xBE, 0x84, 0x80, 0x2B, 0x48, 0x80, 0x40, 0x2A, 0x08, +0x5B, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0x2A, 0x08, 0xA2, 0xC0, 0x04, 0xF0, 0xF0, 0x27, +0x8A, 0x95, 0x2A, 0x08, 0x59, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0x2A, 0x08, 0xA2, 0xC0, +0x05, 0x30, 0xF0, 0x27, 0x8A, 0x95, 0x2A, 0x08, 0x57, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, +0x2A, 0x08, 0xA2, 0xC0, 0x06, 0x30, 0xF0, 0x27, 0x8A, 0x95, 0xA1, 0x01, 0xA1, 0x4A, 0x2A, 0x08, +0xA2, 0xC0, 0x00, 0xB0, 0xF0, 0x27, 0x8A, 0x95, 0xE4, 0xB0, 0xAB, 0x40, 0xE7, 0xAD, 0xE8, 0x2D, +0xAB, 0x0B, 0xE6, 0x6D, 0xEB, 0xAD, 0x00, 0x00, 0x0F, 0xB0, 0x83, 0x52, 0x03, 0x53, 0xA1, 0xC0, +0x2A, 0x08, 0xA2, 0xC0, 0x00, 0xB0, 0xF0, 0x27, 0x8A, 0x95, 0xD0, 0x70, 0xAC, 0x00, 0xC9, 0xB0, +0xAB, 0x40, 0xAB, 0x0B, 0xF9, 0xAD, 0xAC, 0xCB, 0xF9, 0xAD, 0xFE, 0xED, 0x83, 0x52, 0x03, 0x53, +0x2A, 0x08, 0x03, 0xAD, 0xAC, 0x00, 0x2C, 0x08, 0x03, 0x59, 0x0A, 0xAE, 0xAD, 0x81, 0xAD, 0xCA, +0xAE, 0x81, 0x0D, 0xEE, 0xAD, 0x81, 0xAE, 0x81, 0xAE, 0xCA, 0x83, 0x96, 0xED, 0xC1, 0x0E, 0x70, +0x83, 0x52, 0xEF, 0xC0, 0x02, 0xF0, 0xEE, 0x80, 0x2D, 0x48, 0xAA, 0xE6, 0x8A, 0x95, 0x2E, 0x48, +0xAA, 0xE6, 0x8A, 0x95, 0x2D, 0x48, 0xE2, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xAA, 0x00, 0x2E, 0x48, +0x5B, 0xBE, 0x84, 0x80, 0x2A, 0x08, 0x80, 0x40, 0x66, 0x5F, 0x2C, 0xEE, 0x2D, 0x48, 0x55, 0x7E, +0x84, 0x80, 0x00, 0x48, 0x28, 0xFE, 0x31, 0xEE, 0x2D, 0x48, 0x55, 0x7E, 0x84, 0x80, 0x00, 0x48, +0x14, 0xFE, 0xAA, 0x00, 0x2E, 0x48, 0x59, 0x7E, 0x84, 0x80, 0x2A, 0x08, 0x80, 0x40, 0x2D, 0x48, +0x55, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xAA, 0x00, 0x2E, 0x48, 0x59, 0x7E, 0x84, 0x80, 0x00, 0x48, +0xAB, 0x40, 0x2A, 0x08, 0x2B, 0x42, 0x03, 0x18, 0x4F, 0x6E, 0x2E, 0x48, 0x5B, 0xBE, 0x84, 0x80, +0x00, 0x8A, 0xAA, 0x00, 0x2E, 0x48, 0x5B, 0xBE, 0x84, 0x80, 0x2A, 0x08, 0x80, 0x40, 0x2D, 0x48, +0xDC, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xAA, 0x00, 0x2E, 0x48, 0x57, 0xBE, 0x84, 0x80, 0x2A, 0x08, +0x80, 0x40, 0x2E, 0x48, 0x5B, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0x2E, 0x48, 0xA2, 0xC0, +0x04, 0xF0, 0xF0, 0x27, 0x8A, 0x95, 0x2E, 0x48, 0x59, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, +0x2E, 0x48, 0xA2, 0xC0, 0x05, 0x30, 0xF0, 0x27, 0x8A, 0x95, 0x2E, 0x48, 0x57, 0xBE, 0x84, 0x80, +0x00, 0x48, 0xA1, 0xC0, 0x2E, 0x48, 0xA2, 0xC0, 0x06, 0x30, 0xF0, 0x27, 0x8A, 0x95, 0xA1, 0x01, +0xA1, 0x4A, 0x2D, 0x48, 0xA2, 0xC0, 0x00, 0xB0, 0xF0, 0x27, 0x8A, 0x95, 0xA1, 0x01, 0xA1, 0x4A, +0x2E, 0x48, 0xA2, 0xC0, 0x00, 0xB0, 0xF0, 0x27, 0x8A, 0x95, 0xE4, 0xB0, 0xAA, 0x00, 0x88, 0xAE, +0x89, 0xEE, 0xAA, 0xCB, 0x87, 0x2E, 0x8C, 0xEE, 0x00, 0x00, 0x03, 0x30, 0x83, 0x52, 0x03, 0x53, +0xA1, 0xC0, 0x2D, 0x48, 0xA2, 0xC0, 0x00, 0xB0, 0xF0, 0x27, 0x8A, 0x95, 0x0D, 0x70, 0xA1, 0xC0, +0x2E, 0x48, 0xA2, 0xC0, 0x00, 0xB0, 0xF0, 0x27, 0x8A, 0x95, 0xD0, 0x70, 0xAB, 0x40, 0xC9, 0xB0, +0xAA, 0x00, 0xAA, 0xCB, 0xA1, 0xEE, 0xAB, 0x0B, 0xA1, 0xEE, 0xA6, 0x2E, 0x83, 0x52, 0x03, 0x53, +0x2D, 0x48, 0x03, 0xAD, 0xA6, 0x00, 0xE2, 0x7E, 0x84, 0x80, 0x83, 0x93, 0x00, 0x48, 0xA1, 0xC0, +0x26, 0x08, 0xA2, 0xC0, 0x01, 0xF0, 0xF0, 0x27, 0x8A, 0x95, 0x26, 0x08, 0x55, 0x7E, 0x84, 0x80, +0x00, 0x48, 0xA1, 0xC0, 0x26, 0x08, 0xA2, 0xC0, 0x02, 0xF0, 0xF0, 0x27, 0x8A, 0x95, 0x26, 0x08, +0xDC, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0x26, 0x08, 0xA2, 0xC0, 0x03, 0x30, 0xF0, 0x27, +0x8A, 0x95, 0x26, 0x08, 0x5B, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0x26, 0x08, 0xA2, 0xC0, +0x04, 0xF0, 0xF0, 0x27, 0x8A, 0x95, 0x26, 0x08, 0x59, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, +0x26, 0x08, 0xA2, 0xC0, 0x05, 0x30, 0xF0, 0x27, 0x8A, 0x95, 0x26, 0x08, 0x57, 0xBE, 0x84, 0x80, +0x00, 0x48, 0xA1, 0xC0, 0x26, 0x08, 0xA2, 0xC0, 0x06, 0x30, 0xF0, 0x27, 0x8A, 0x95, 0x6E, 0x0E, +0xF0, 0x39, 0x6F, 0xC4, 0xA1, 0xC0, 0x26, 0x08, 0xA2, 0xC0, 0x08, 0xF0, 0xF0, 0x27, 0x8A, 0x95, +0x62, 0x08, 0xA4, 0xC0, 0x04, 0xF0, 0x03, 0xD0, 0xA4, 0x8D, 0xFF, 0x7E, 0x03, 0xD0, 0x03, 0x9D, +0xF4, 0x6E, 0x24, 0x4D, 0x60, 0xC4, 0xA1, 0xC0, 0x26, 0x08, 0xA2, 0xC0, 0x0A, 0x30, 0xF0, 0x27, +0x8A, 0x95, 0x6D, 0x88, 0xA1, 0xC0, 0x26, 0x08, 0xA2, 0xC0, 0x0B, 0x70, 0xF0, 0x27, 0x8A, 0x95, +0x6C, 0x48, 0xA4, 0xC0, 0x05, 0x30, 0x03, 0xD0, 0xA4, 0x8D, 0xFF, 0x7E, 0x03, 0xD0, 0x03, 0x9D, +0x0C, 0xEF, 0x6B, 0x0D, 0x24, 0xC4, 0x68, 0x04, 0xA1, 0xC0, 0x26, 0x08, 0xA2, 0xC0, 0x0C, 0x30, +0xF0, 0x27, 0x8A, 0x95, 0x69, 0x48, 0xA4, 0xC0, 0x05, 0x30, 0x03, 0xD0, 0xA4, 0x8D, 0xFF, 0x7E, +0x03, 0x9D, 0x1D, 0x6F, 0x6A, 0x48, 0xA5, 0x00, 0x01, 0xF0, 0x03, 0xD0, 0xA5, 0xCD, 0xFF, 0x7E, +0x03, 0xD0, 0x03, 0x9D, 0x26, 0x2F, 0x25, 0x8D, 0x24, 0xC4, 0x5E, 0x84, 0xA1, 0xC0, 0x26, 0x08, +0xA2, 0xC0, 0x0D, 0x70, 0xF0, 0x27, 0x8A, 0x95, 0x03, 0xD0, 0x5F, 0x4D, 0x5D, 0x84, 0xA1, 0xC0, +0x26, 0x08, 0xA2, 0xC0, 0x0E, 0x70, 0xF0, 0x27, 0x8A, 0x95, 0x83, 0x96, 0x6C, 0x48, 0x83, 0x52, +0xA4, 0xC0, 0x03, 0xD0, 0xA4, 0x8D, 0x03, 0xD0, 0xA4, 0x8D, 0x03, 0xD0, 0xA4, 0x8D, 0x83, 0x96, +0x6D, 0x88, 0x83, 0x52, 0xA5, 0x00, 0x03, 0xD0, 0xA5, 0xCD, 0x03, 0xD0, 0xA5, 0xCD, 0x83, 0x96, +0x03, 0xD0, 0x6E, 0x0D, 0x83, 0x52, 0x25, 0x04, 0x24, 0xC4, 0x61, 0x04, 0xA1, 0xC0, 0x26, 0x08, +0xA2, 0xC0, 0x10, 0xF0, 0xF0, 0x6F, 0xA4, 0x01, 0x24, 0xC8, 0xEA, 0xBE, 0x84, 0x80, 0x83, 0x93, +0x00, 0x48, 0xA1, 0xC0, 0x24, 0xC8, 0xA2, 0xC0, 0x00, 0xB0, 0xF0, 0x27, 0x8A, 0x95, 0x24, 0xC8, +0xE0, 0x3E, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0x24, 0xC8, 0xA2, 0xC0, 0x01, 0xF0, 0xF0, 0x27, +0x8A, 0x95, 0x24, 0xC8, 0xDE, 0xFE, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0x24, 0xC8, 0xA2, 0xC0, +0x02, 0xF0, 0xF0, 0x27, 0x8A, 0x95, 0x24, 0xC8, 0xDA, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, +0x24, 0xC8, 0xA2, 0xC0, 0x03, 0x30, 0xF0, 0x27, 0x8A, 0x95, 0x24, 0xC8, 0xE8, 0x7E, 0x84, 0x80, +0x00, 0x48, 0xA1, 0xC0, 0x24, 0xC8, 0xA2, 0xC0, 0x04, 0xF0, 0xF0, 0x27, 0x8A, 0x95, 0x24, 0xC8, +0xE6, 0xBE, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0x24, 0xC8, 0xA2, 0xC0, 0x05, 0x30, 0xF0, 0x27, +0x8A, 0x95, 0x24, 0xC8, 0xE4, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0x24, 0xC8, 0xA2, 0xC0, +0x06, 0x30, 0xF0, 0x27, 0x8A, 0x95, 0x24, 0xC8, 0xAC, 0x7E, 0x84, 0x80, 0x83, 0xD7, 0x00, 0x48, +0xA1, 0xC0, 0x24, 0xC8, 0xA2, 0xC0, 0x08, 0xF0, 0xF0, 0x27, 0x8A, 0x95, 0x24, 0xC8, 0xA8, 0x3E, +0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0x24, 0xC8, 0xA2, 0xC0, 0x0A, 0x30, 0xF0, 0x27, 0x8A, 0x95, +0x24, 0xC8, 0xAA, 0x7E, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0x24, 0xC8, 0xA2, 0xC0, 0x0B, 0x70, +0xF0, 0x27, 0x8A, 0x95, 0x24, 0xC8, 0xA2, 0x3E, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0x24, 0xC8, +0xA2, 0xC0, 0x0C, 0x30, 0xF0, 0x27, 0x8A, 0x95, 0x24, 0xC8, 0xA4, 0x3E, 0x84, 0x80, 0x00, 0x48, +0xA1, 0xC0, 0x24, 0xC8, 0xA2, 0xC0, 0x0D, 0x70, 0xF0, 0x27, 0x8A, 0x95, 0x24, 0xC8, 0xA6, 0x7E, +0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0x24, 0xC8, 0xA2, 0xC0, 0x0E, 0x70, 0xF0, 0x27, 0x8A, 0x95, +0x24, 0xC8, 0xA0, 0xFE, 0x84, 0x80, 0x00, 0x48, 0xA1, 0xC0, 0x24, 0xC8, 0xA2, 0xC0, 0x10, 0xF0, +0xF0, 0x27, 0x8A, 0x95, 0x02, 0xF0, 0xA4, 0x4A, 0x24, 0xC2, 0x03, 0x18, 0x08, 0x40, 0x5C, 0x6F, +0xA3, 0x00, 0x22, 0xC8, 0x03, 0x59, 0xFA, 0xEF, 0x21, 0xC8, 0x88, 0x80, 0x23, 0x08, 0x80, 0x38, +0x86, 0xC0, 0x08, 0x40, 0x21, 0xC8, 0x88, 0x80, 0x23, 0x08, 0x80, 0x38, 0x85, 0xC0, 0x08, 0x40 +}; diff --git a/libloragw/src/loragw_aux.c b/libloragw/src/loragw_aux.c new file mode 100644 index 0000000..c921296 --- /dev/null +++ b/libloragw/src/loragw_aux.c @@ -0,0 +1,61 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + LoRa concentrator HAL auxiliary functions + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +/* fix an issue between POSIX and C99 */ +#if __STDC_VERSION__ >= 199901L + #define _XOPEN_SOURCE 600 +#else + #define _XOPEN_SOURCE 500 +#endif + +#include <stdio.h> /* printf fprintf */ +#include <time.h> /* clock_nanosleep */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#if DEBUG_AUX == 1 + #define DEBUG_MSG(str) fprintf(stderr, str) + #define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args) +#else + #define DEBUG_MSG(str) + #define DEBUG_PRINTF(fmt, args...) +#endif + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ + +/* This implementation is POSIX-pecific and require a fix to be compatible with C99 */ +void wait_ms(unsigned long a) { + struct timespec dly; + struct timespec rem; + + dly.tv_sec = a / 1000; + dly.tv_nsec = ((long)a % 1000) * 1000000; + + DEBUG_PRINTF("NOTE dly: %ld sec %ld ns\n", dly.tv_sec, dly.tv_nsec); + + if((dly.tv_sec > 0) || ((dly.tv_sec == 0) && (dly.tv_nsec > 100000))) { + clock_nanosleep(CLOCK_MONOTONIC, 0, &dly, &rem); + DEBUG_PRINTF("NOTE remain: %ld sec %ld ns\n", rem.tv_sec, rem.tv_nsec); + } + return; +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/src/loragw_fpga.c b/libloragw/src/loragw_fpga.c new file mode 100644 index 0000000..465f43e --- /dev/null +++ b/libloragw/src/loragw_fpga.c @@ -0,0 +1,352 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Functions used to handle FPGA register access for LoRa concentrator. + Registers are addressed by name. + Multi-bytes registers are handled automatically. + Read-modify-write is handled automatically. + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Michael Coracin +*/ + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ +#include <stdio.h> /* printf fprintf */ + +#include "loragw_spi.h" +#include "loragw_aux.h" +#include "loragw_hal.h" +#include "loragw_reg.h" +#include "loragw_fpga.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#if DEBUG_REG == 1 + #define DEBUG_MSG(str) fprintf(stderr, str) + #define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args) + #define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_REG_ERROR;} +#else + #define DEBUG_MSG(str) + #define DEBUG_PRINTF(fmt, args...) + #define CHECK_NULL(a) if(a==NULL){return LGW_REG_ERROR;} +#endif + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +/* +auto generated register mapping for C code : 11-Jul-2013 13:20:40 +this file contains autogenerated C struct used to access the LoRa register from the Primer firmware +this file is autogenerated from registers description +293 registers are defined +*/ +const struct lgw_reg_s fpga_regs[LGW_FPGA_TOTALREGS] = { + {-1,0,0,0,1,0,0}, /* SOFT_RESET */ + {-1,0,1,0,4,1,0}, /* FPGA_FEATURE */ + {-1,0,5,0,3,1,0}, /* LBT_INITIAL_FREQ */ + {-1,1,0,0,8,1,0}, /* VERSION */ + {-1,2,0,0,8,1,0}, /* FPGA_STATUS */ + {-1,3,0,0,1,0,0}, /* FPGA_CTRL_FEATURE_START */ + {-1,3,1,0,1,0,0}, /* FPGA_CTRL_RADIO_RESET */ + {-1,3,2,0,1,0,0}, /* FPGA_CTRL_INPUT_SYNC_I */ + {-1,3,3,0,1,0,0}, /* FPGA_CTRL_INPUT_SYNC_Q */ + {-1,3,4,0,1,0,0}, /* FPGA_CTRL_OUTPUT_SYNC */ + {-1,3,5,0,1,0,0}, /* FPGA_CTRL_INVERT_IQ */ + {-1,3,6,0,1,0,0}, /* FPGA_CTRL_ACCESS_HISTO_MEM */ + {-1,3,7,0,1,0,0}, /* FPGA_CTRL_CLEAR_HISTO_MEM */ + {-1,4,0,0,8,0,0}, /* HISTO_RAM_ADDR */ + {-1,5,0,0,8,1,0}, /* HISTO_RAM_DATA */ + {-1,8,0,0,16,0,1000}, /* HISTO_NB_READ */ + {-1,14,0,0,16,1,0}, /* LBT_TIMESTAMP_CH */ + {-1,17,0,0,4,0,0}, /* LBT_TIMESTAMP_SELECT_CH */ + {-1,18,0,0,8,0,0}, /* LBT_CH0_FREQ_OFFSET */ + {-1,19,0,0,8,0,0}, /* LBT_CH1_FREQ_OFFSET */ + {-1,20,0,0,8,0,0}, /* LBT_CH2_FREQ_OFFSET */ + {-1,21,0,0,8,0,0}, /* LBT_CH3_FREQ_OFFSET */ + {-1,22,0,0,8,0,0}, /* LBT_CH4_FREQ_OFFSET */ + {-1,23,0,0,8,0,0}, /* LBT_CH5_FREQ_OFFSET */ + {-1,24,0,0,8,0,0}, /* LBT_CH6_FREQ_OFFSET */ + {-1,25,0,0,8,0,0}, /* LBT_CH7_FREQ_OFFSET */ + {-1,26,0,0,8,0,0}, /* SCAN_FREQ_OFFSET */ + {-1,28,0,0,1,0,0}, /* LBT_SCAN_TIME_CH0 */ + {-1,28,1,0,1,0,0}, /* LBT_SCAN_TIME_CH1 */ + {-1,28,2,0,1,0,0}, /* LBT_SCAN_TIME_CH2 */ + {-1,28,3,0,1,0,0}, /* LBT_SCAN_TIME_CH3 */ + {-1,28,4,0,1,0,0}, /* LBT_SCAN_TIME_CH4 */ + {-1,28,5,0,1,0,0}, /* LBT_SCAN_TIME_CH5 */ + {-1,28,6,0,1,0,0}, /* LBT_SCAN_TIME_CH6 */ + {-1,28,7,0,1,0,0}, /* LBT_SCAN_TIME_CH7 */ + {-1,30,0,0,8,0,160}, /* RSSI_TARGET */ + {-1,31,0,0,24,0,0}, /* HISTO_SCAN_FREQ */ + {-1,34,0,0,6,0,0} /* NOTCH_FREQ_OFFSET */ +}; + +/* -------------------------------------------------------------------------- */ +/* --- INTERNAL SHARED VARIABLES -------------------------------------------- */ + +extern void *lgw_spi_target; /*! generic pointer to the SPI device */ +extern uint8_t lgw_spi_mux_mode; /*! current SPI mux mode used */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE VARIABLES ---------------------------------------------------- */ +static bool tx_notch_support = false; +static uint8_t tx_notch_offset; + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS ---------------------------------------------------- */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ + +float lgw_fpga_get_tx_notch_delay(void) { + float tx_notch_delay; + + if (tx_notch_support == false) { + return 0; + } + + /* Notch filtering performed by FPGA adds a constant delay (group delay) that we need to compensate */ + tx_notch_delay = (31.25 * ((64 + tx_notch_offset) / 2)) / 1E3; /* 32MHz => 31.25ns */ + + return tx_notch_delay; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_fpga_configure(uint32_t tx_notch_freq) { + int x; + int32_t val; + bool spectral_scan_support, lbt_support; + + /* Check input parameters */ + if ((tx_notch_freq < LGW_MIN_NOTCH_FREQ) || (tx_notch_freq > LGW_MAX_NOTCH_FREQ)) { + DEBUG_PRINTF("WARNING: FPGA TX notch frequency is out of range (%u - [%u..%u]), setting it to default (%u)\n", tx_notch_freq, LGW_MIN_NOTCH_FREQ, LGW_MAX_NOTCH_FREQ, LGW_DEFAULT_NOTCH_FREQ); + tx_notch_freq = LGW_DEFAULT_NOTCH_FREQ; + } + + /* Get supported FPGA features */ + printf("INFO: FPGA supported features:"); + lgw_fpga_reg_r(LGW_FPGA_FEATURE, &val); + tx_notch_support = TAKE_N_BITS_FROM((uint8_t)val, 0, 1); + if (tx_notch_support == true) { + printf(" [TX filter] "); + } + spectral_scan_support = TAKE_N_BITS_FROM((uint8_t)val, 1, 1); + if (spectral_scan_support == true) { + printf(" [Spectral Scan] "); + } + lbt_support = TAKE_N_BITS_FROM((uint8_t)val, 2, 1); + if (lbt_support == true) { + printf(" [LBT] "); + } + printf("\n"); + + x = lgw_fpga_reg_w(LGW_FPGA_CTRL_INPUT_SYNC_I, 1); + x |= lgw_fpga_reg_w(LGW_FPGA_CTRL_INPUT_SYNC_Q, 1); + x |= lgw_fpga_reg_w(LGW_FPGA_CTRL_OUTPUT_SYNC, 0); + if (x != LGW_REG_SUCCESS) { + DEBUG_MSG("ERROR: Failed to configure FPGA TX synchro\n"); + return LGW_REG_ERROR; + } + + /* Required for Semtech AP2 reference design */ + x = lgw_fpga_reg_w(LGW_FPGA_CTRL_INVERT_IQ, 1); + if (x != LGW_REG_SUCCESS) { + DEBUG_MSG("ERROR: Failed to configure FPGA polarity\n"); + return LGW_REG_ERROR; + } + + /* Configure TX notch filter */ + if (tx_notch_support == true) { + tx_notch_offset = (32E6 / (2*tx_notch_freq)) - 64; + x = lgw_fpga_reg_w(LGW_FPGA_NOTCH_FREQ_OFFSET, (int32_t)tx_notch_offset); + if (x != LGW_REG_SUCCESS) { + DEBUG_MSG("ERROR: Failed to configure FPGA TX notch filter\n"); + return LGW_REG_ERROR; + } + + /* Readback to check that notch frequency is programmable */ + x = lgw_fpga_reg_r(LGW_FPGA_NOTCH_FREQ_OFFSET, &val); + if (x != LGW_REG_SUCCESS) { + DEBUG_MSG("ERROR: Failed to read FPGA TX notch frequency\n"); + return LGW_REG_ERROR; + } + if (val != tx_notch_offset) { + DEBUG_MSG("WARNING: TX notch filter frequency is not programmable (check your FPGA image)\n"); + } else { + DEBUG_PRINTF("INFO: TX notch filter frequency set to %u (%i)\n", tx_notch_freq, tx_notch_offset); + } + } + + return LGW_REG_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* Write to a register addressed by name */ +int lgw_fpga_reg_w(uint16_t register_id, int32_t reg_value) { + int spi_stat = LGW_SPI_SUCCESS; + struct lgw_reg_s r; + + /* check input parameters */ + if (register_id >= LGW_FPGA_TOTALREGS) { + DEBUG_MSG("ERROR: REGISTER NUMBER OUT OF DEFINED RANGE\n"); + return LGW_REG_ERROR; + } + + /* check if SPI is initialised */ + if (lgw_spi_target == NULL) { + DEBUG_MSG("ERROR: CONCENTRATOR UNCONNECTED\n"); + return LGW_REG_ERROR; + } + + /* get register struct from the struct array */ + r = fpga_regs[register_id]; + + /* reject write to read-only registers */ + if (r.rdon == 1){ + DEBUG_MSG("ERROR: TRYING TO WRITE A READ-ONLY REGISTER\n"); + return LGW_REG_ERROR; + } + + spi_stat += reg_w_align32(lgw_spi_target, LGW_SPI_MUX_MODE1, LGW_SPI_MUX_TARGET_FPGA, r, reg_value); + + if (spi_stat != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR: SPI ERROR DURING REGISTER WRITE\n"); + return LGW_REG_ERROR; + } else { + return LGW_REG_SUCCESS; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* Read to a register addressed by name */ +int lgw_fpga_reg_r(uint16_t register_id, int32_t *reg_value) { + int spi_stat = LGW_SPI_SUCCESS; + struct lgw_reg_s r; + + /* check input parameters */ + CHECK_NULL(reg_value); + if (register_id >= LGW_FPGA_TOTALREGS) { + DEBUG_MSG("ERROR: REGISTER NUMBER OUT OF DEFINED RANGE\n"); + return LGW_REG_ERROR; + } + + /* check if SPI is initialised */ + if (lgw_spi_target == NULL) { + DEBUG_MSG("ERROR: CONCENTRATOR UNCONNECTED\n"); + return LGW_REG_ERROR; + } + + /* get register struct from the struct array */ + r = fpga_regs[register_id]; + + spi_stat += reg_r_align32(lgw_spi_target, LGW_SPI_MUX_MODE1, LGW_SPI_MUX_TARGET_FPGA, r, reg_value); + + if (spi_stat != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR: SPI ERROR DURING REGISTER WRITE\n"); + return LGW_REG_ERROR; + } else { + return LGW_REG_SUCCESS; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* Point to a register by name and do a burst write */ +int lgw_fpga_reg_wb(uint16_t register_id, uint8_t *data, uint16_t size) { + int spi_stat = LGW_SPI_SUCCESS; + struct lgw_reg_s r; + + /* check input parameters */ + CHECK_NULL(data); + if (size == 0) { + DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n"); + return LGW_REG_ERROR; + } + if (register_id >= LGW_FPGA_TOTALREGS) { + DEBUG_MSG("ERROR: REGISTER NUMBER OUT OF DEFINED RANGE\n"); + return LGW_REG_ERROR; + } + + /* check if SPI is initialised */ + if (lgw_spi_target == NULL) { + DEBUG_MSG("ERROR: CONCENTRATOR UNCONNECTED\n"); + return LGW_REG_ERROR; + } + + /* get register struct from the struct array */ + r = fpga_regs[register_id]; + + /* reject write to read-only registers */ + if (r.rdon == 1){ + DEBUG_MSG("ERROR: TRYING TO BURST WRITE A READ-ONLY REGISTER\n"); + return LGW_REG_ERROR; + } + + /* do the burst write */ + spi_stat += lgw_spi_wb(lgw_spi_target, LGW_SPI_MUX_MODE1, LGW_SPI_MUX_TARGET_FPGA, r.addr, data, size); + + if (spi_stat != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR: SPI ERROR DURING REGISTER BURST WRITE\n"); + return LGW_REG_ERROR; + } else { + return LGW_REG_SUCCESS; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* Point to a register by name and do a burst read */ +int lgw_fpga_reg_rb(uint16_t register_id, uint8_t *data, uint16_t size) { + int spi_stat = LGW_SPI_SUCCESS; + struct lgw_reg_s r; + + /* check input parameters */ + CHECK_NULL(data); + if (size == 0) { + DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n"); + return LGW_REG_ERROR; + } + if (register_id >= LGW_FPGA_TOTALREGS) { + DEBUG_MSG("ERROR: REGISTER NUMBER OUT OF DEFINED RANGE\n"); + return LGW_REG_ERROR; + } + + /* check if SPI is initialised */ + if (lgw_spi_target == NULL) { + DEBUG_MSG("ERROR: CONCENTRATOR UNCONNECTED\n"); + return LGW_REG_ERROR; + } + + /* get register struct from the struct array */ + r = fpga_regs[register_id]; + + /* do the burst read */ + spi_stat += lgw_spi_rb(lgw_spi_target, LGW_SPI_MUX_MODE1, LGW_SPI_MUX_TARGET_FPGA, r.addr, data, size); + + if (spi_stat != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR: SPI ERROR DURING REGISTER BURST READ\n"); + return LGW_REG_ERROR; + } else { + return LGW_REG_SUCCESS; + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/src/loragw_gps.c b/libloragw/src/loragw_gps.c new file mode 100644 index 0000000..c0e0ded --- /dev/null +++ b/libloragw/src/loragw_gps.c @@ -0,0 +1,835 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Library of functions to manage a GNSS module (typically GPS) for accurate + timestamping of packets and synchronisation of gateways. + A limited set of module brands/models are supported. + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Michael Coracin +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#define _GNU_SOURCE /* needed for qsort_r to be defined */ +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ +#include <stdio.h> /* printf fprintf */ +#include <string.h> /* memcpy */ + +#include <time.h> /* struct timespec */ +#include <fcntl.h> /* open */ +#include <termios.h> /* tcflush */ +#include <math.h> /* modf */ + +#include <stdlib.h> + +#include "loragw_gps.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#if DEBUG_GPS == 1 + #define DEBUG_MSG(args...) fprintf(stderr, args) + #define DEBUG_ARRAY(a,b,c) for(a=0;a<b;++a) fprintf(stderr,"%x.",c[a]);fprintf(stderr,"end\n") + #define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_GPS_ERROR;} +#else + #define DEBUG_MSG(args...) + #define DEBUG_ARRAY(a,b,c) for(a=0;a!=0;){} + #define CHECK_NULL(a) if(a==NULL){return LGW_GPS_ERROR;} +#endif +#define TRACE() fprintf(stderr, "@ %s %d\n", __FUNCTION__, __LINE__); + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +#define TS_CPS 1E6 /* count-per-second of the timestamp counter */ +#define PLUS_10PPM 1.00001 +#define MINUS_10PPM 0.99999 +#define DEFAULT_BAUDRATE B9600 + +#define UBX_MSG_NAVTIMEGPS_LEN 16 + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE VARIABLES ---------------------------------------------------- */ + + +/* result of the NMEA parsing */ +static short gps_yea = 0; /* year (2 or 4 digits) */ +static short gps_mon = 0; /* month (1-12) */ +static short gps_day = 0; /* day of the month (1-31) */ +static short gps_hou = 0; /* hours (0-23) */ +static short gps_min = 0; /* minutes (0-59) */ +static short gps_sec = 0; /* seconds (0-60)(60 is for leap second) */ +static float gps_fra = 0.0; /* fractions of seconds (<1) */ +static bool gps_time_ok = false; +static int16_t gps_week = 0; /* GPS week number of the navigation epoch */ +static uint32_t gps_iTOW = 0; /* GPS time of week in milliseconds */ +static int32_t gps_fTOW = 0; /* Fractional part of iTOW (+/-500000) in nanosec */ + +static short gps_dla = 0; /* degrees of latitude */ +static double gps_mla = 0.0; /* minutes of latitude */ +static char gps_ola = 0; /* orientation (N-S) of latitude */ +static short gps_dlo = 0; /* degrees of longitude */ +static double gps_mlo = 0.0; /* minutes of longitude */ +static char gps_olo = 0; /* orientation (E-W) of longitude */ +static short gps_alt = 0; /* altitude */ +static bool gps_pos_ok = false; + +static char gps_mod = 'N'; /* GPS mode (N no fix, A autonomous, D differential) */ +static short gps_sat = 0; /* number of satellites used for fix */ + +static struct termios ttyopt_restore; + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ + +static int nmea_checksum(const char *nmea_string, int buff_size, char *checksum); + +static char nibble_to_hexchar(uint8_t a); + +static bool validate_nmea_checksum(const char *serial_buff, int buff_size); + +static bool match_label(const char *s, char *label, int size, char wildcard); + +static int str_chop(char *s, int buff_size, char separator, int *idx_ary, int max_idx); + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ + +/* +Calculate the checksum for a NMEA string +Skip the first '$' if necessary and calculate checksum until '*' character is +reached (or buff_size exceeded). +Checksum must point to a 2-byte (or more) char array. +Return position of the checksum in the string +*/ +static int nmea_checksum(const char *nmea_string, int buff_size, char *checksum) { + int i = 0; + uint8_t check_num = 0; + + /* check input parameters */ + if ((nmea_string == NULL) || (checksum == NULL) || (buff_size <= 1)) { + DEBUG_MSG("Invalid parameters for nmea_checksum\n"); + return -1; + } + + /* skip the first '$' if necessary */ + if (nmea_string[i] == '$') { + i += 1; + } + + /* xor until '*' or max length is reached */ + while (nmea_string[i] != '*') { + check_num ^= nmea_string[i]; + i += 1; + if (i >= buff_size) { + DEBUG_MSG("Maximum length reached for nmea_checksum\n"); + return -1; + } + } + + /* Convert checksum value to 2 hexadecimal characters */ + checksum[0] = nibble_to_hexchar(check_num / 16); /* upper nibble */ + checksum[1] = nibble_to_hexchar(check_num % 16); /* lower nibble */ + + return i + 1; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static char nibble_to_hexchar(uint8_t a) { + if (a < 10) { + return '0' + a; + } else if (a < 16) { + return 'A' + (a-10); + } else { + return '?'; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* +Calculate the checksum of a NMEA frame and compare it to the checksum that is +present at the end of it. +Return true if it matches +*/ +static bool validate_nmea_checksum(const char *serial_buff, int buff_size) { + int checksum_index; + char checksum[2]; /* 2 characters to calculate NMEA checksum */ + + checksum_index = nmea_checksum(serial_buff, buff_size, checksum); + + /* could we calculate a verification checksum ? */ + if (checksum_index < 0) { + DEBUG_MSG("ERROR: IMPOSSIBLE TO PARSE NMEA SENTENCE\n"); + return false; + } + + /* check if there are enough char in the serial buffer to read checksum */ + if (checksum_index >= (buff_size - 2)) { + DEBUG_MSG("ERROR: IMPOSSIBLE TO READ NMEA SENTENCE CHECKSUM\n"); + return false; + } + + /* check the checksum per se */ + if ((serial_buff[checksum_index] == checksum[0]) && (serial_buff[checksum_index+1] == checksum[1])) { + return true; + } else { + DEBUG_MSG("ERROR: NMEA CHECKSUM %c%c DOESN'T MATCH VERIFICATION CHECKSUM %c%c\n", serial_buff[checksum_index], serial_buff[checksum_index+1], checksum[0], checksum[1]); + return false; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* +Return true if the "label" string (can contain wildcard characters) matches +the begining of the "s" string +*/ +static bool match_label(const char *s, char *label, int size, char wildcard) { + int i; + + for (i=0; i < size; i++) { + if (label[i] == wildcard) continue; + if (label[i] != s[i]) return false; + } + return true; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* +Chop a string into smaller strings +Replace every separator in the input character buffer by a null character so +that all s[index] are valid strings. +Populate an array of integer 'idx_ary' representing indexes of token in the +string. +buff_size and max_idx are there to prevent segfaults. +Return the number of token found (number of idx_ary filled). +*/ +int str_chop(char *s, int buff_size, char separator, int *idx_ary, int max_idx) { + int i = 0; /* index in the string */ + int j = 0; /* index in the result array */ + + if ((s == NULL) || (buff_size < 0) || (separator == 0) || (idx_ary == NULL) || (max_idx < 0)) { + /* unsafe to do anything */ + return -1; + } + if ((buff_size == 0) || (max_idx == 0)) { + /* nothing to do */ + return 0; + } + s[buff_size - 1] = 0; /* add string terminator at the end of the buffer, just to be sure */ + idx_ary[j] = 0; + j += 1; + /* loop until string terminator is reached */ + while (s[i] != 0) { + if (s[i] == separator) { + s[i] = 0; /* replace separator by string terminator */ + if (j >= max_idx) { /* no more room in the index array */ + return j; + } + idx_ary[j] = i+1; /* next token start after replaced separator */ + ++j; + } + ++i; + } + return j; +} + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ + +int lgw_gps_enable(char *tty_path, char *gps_family, speed_t target_brate, int *fd_ptr) { + int i; + struct termios ttyopt; /* serial port options */ + int gps_tty_dev; /* file descriptor to the serial port of the GNSS module */ + uint8_t ubx_cmd_timegps[UBX_MSG_NAVTIMEGPS_LEN] = { + 0xB5, 0x62, /* UBX Sync Chars */ + 0x06, 0x01, /* CFG-MSG Class/ID */ + 0x08, 0x00, /* Payload length */ + 0x01, 0x20, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, /* Enable NAV-TIMEGPS output on serial */ + 0x32, 0x94 }; /* Checksum */ + ssize_t num_written; + + /* check input parameters */ + CHECK_NULL(tty_path); + CHECK_NULL(fd_ptr); + + /* open TTY device */ + gps_tty_dev = open(tty_path, O_RDWR | O_NOCTTY); + if (gps_tty_dev <= 0) { + DEBUG_MSG("ERROR: TTY PORT FAIL TO OPEN, CHECK PATH AND ACCESS RIGHTS\n"); + return LGW_GPS_ERROR; + } + *fd_ptr = gps_tty_dev; + + /* manage the different GPS modules families */ + if (gps_family == NULL) { + DEBUG_MSG("WARNING: this version of GPS module may not be supported\n"); + } else if (strncmp(gps_family, "ubx7", 4) != 0) { + /* The current implementation relies on proprietary messages from U-Blox */ + /* GPS modules (UBX, NAV-TIMEGPS...) and has only be tested with a u-blox 7. */ + /* Those messages allow to get NATIVE GPS time (no leap seconds) required */ + /* for class-B handling and GPS synchronization */ + /* see lgw_parse_ubx() function for details */ + DEBUG_MSG("WARNING: this version of GPS module may not be supported\n"); + } + + /* manage the target bitrate */ + if (target_brate != 0) { + DEBUG_MSG("WARNING: target_brate parameter ignored for now\n"); // TODO + } + + /* get actual serial port configuration */ + i = tcgetattr(gps_tty_dev, &ttyopt); + if (i != 0) { + DEBUG_MSG("ERROR: IMPOSSIBLE TO GET TTY PORT CONFIGURATION\n"); + return LGW_GPS_ERROR; + } + + /* Save current serial port configuration for restoring later */ + memcpy(&ttyopt_restore, &ttyopt, sizeof ttyopt); + + /* update baudrates */ + cfsetispeed(&ttyopt, DEFAULT_BAUDRATE); + cfsetospeed(&ttyopt, DEFAULT_BAUDRATE); + + /* update terminal parameters */ + /* The following configuration should allow to: + - Get ASCII NMEA messages + - Get UBX binary messages + - Send UBX binary commands + Note: as binary data have to be read/written, we need to disable + various character processing to avoid loosing data */ + /* Control Modes */ + ttyopt.c_cflag |= CLOCAL; /* local connection, no modem control */ + ttyopt.c_cflag |= CREAD; /* enable receiving characters */ + ttyopt.c_cflag |= CS8; /* 8 bit frames */ + ttyopt.c_cflag &= ~PARENB; /* no parity */ + ttyopt.c_cflag &= ~CSTOPB; /* one stop bit */ + /* Input Modes */ + ttyopt.c_iflag |= IGNPAR; /* ignore bytes with parity errors */ + ttyopt.c_iflag &= ~ICRNL; /* do not map CR to NL on input*/ + ttyopt.c_iflag &= ~IGNCR; /* do not ignore carriage return on input */ + ttyopt.c_iflag &= ~IXON; /* disable Start/Stop output control */ + ttyopt.c_iflag &= ~IXOFF; /* do not send Start/Stop characters */ + /* Output Modes */ + ttyopt.c_oflag = 0; /* disable everything on output as we only write binary */ + /* Local Modes */ + ttyopt.c_lflag &= ~ICANON; /* disable canonical input - cannot use with binary input */ + ttyopt.c_lflag &= ~ISIG; /* disable check for INTR, QUIT, SUSP special characters */ + ttyopt.c_lflag &= ~IEXTEN; /* disable any special control character */ + ttyopt.c_lflag &= ~ECHO; /* do not echo back every character typed */ + ttyopt.c_lflag &= ~ECHOE; /* does not erase the last character in current line */ + ttyopt.c_lflag &= ~ECHOK; /* do not echo NL after KILL character */ + + /* settings for non-canonical mode + read will block for until the lesser of VMIN or requested chars have been received */ + ttyopt.c_cc[VMIN] = LGW_GPS_MIN_MSG_SIZE; + ttyopt.c_cc[VTIME] = 0; + + /* set new serial ports parameters */ + i = tcsetattr(gps_tty_dev, TCSANOW, &ttyopt); + if (i != 0){ + DEBUG_MSG("ERROR: IMPOSSIBLE TO UPDATE TTY PORT CONFIGURATION\n"); + return LGW_GPS_ERROR; + } + tcflush(gps_tty_dev, TCIOFLUSH); + + /* Send UBX CFG NAV-TIMEGPS message to tell GPS module to output native GPS time */ + /* This is a binary message, serial port has to be properly configured to handle this */ + num_written = write (gps_tty_dev, ubx_cmd_timegps, UBX_MSG_NAVTIMEGPS_LEN); + if (num_written != UBX_MSG_NAVTIMEGPS_LEN) { + DEBUG_MSG("ERROR: Failed to write on serial port (written=%d)\n", (int) num_written); + } + + /* get timezone info */ + tzset(); + + /* initialize global variables */ + gps_time_ok = false; + gps_pos_ok = false; + gps_mod = 'N'; + + return LGW_GPS_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_gps_disable(int fd) { + int i; + + /* restore serial ports parameters */ + i = tcsetattr(fd, TCSANOW, &ttyopt_restore); + if (i != 0){ + DEBUG_MSG("ERROR: IMPOSSIBLE TO RESTORE TTY PORT CONFIGURATION\n"); + return LGW_GPS_ERROR; + } + tcflush(fd, TCIOFLUSH); + + i = close(fd); + if (i <= 0) { + DEBUG_MSG("ERROR: TTY PORT FAIL TO CLOSE\n"); + return LGW_GPS_ERROR; + } + + return LGW_GPS_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +enum gps_msg lgw_parse_ubx(const char *serial_buff, size_t buff_size, size_t *msg_size) { + bool valid = 0; /* iTOW, fTOW and week validity */ + unsigned int payload_length; + uint8_t ck_a, ck_b; + uint8_t ck_a_rcv, ck_b_rcv; + unsigned int i; + + *msg_size = 0; /* ensure msg_size alway receives a value */ + + /* check input parameters */ + if (serial_buff == NULL) { + return IGNORED; + } + if (buff_size < 8) { + DEBUG_MSG("ERROR: TOO SHORT TO BE A VALID UBX MESSAGE\n"); + return IGNORED; + } + + /* display received serial data and checksum */ + DEBUG_MSG("Note: parsing UBX frame> "); + for (i=0; i<buff_size; i++) { + DEBUG_MSG("%02x ", serial_buff[i]); + } + DEBUG_MSG("\n"); + + /* Check for UBX sync chars 0xB5 0x62 */ + if ((serial_buff[0] == (char)0xB5) && (serial_buff[1] == (char)0x62)) { + + /* Get payload length to compute message size */ + payload_length = (uint8_t)serial_buff[4]; + payload_length |= (uint8_t)serial_buff[5] << 8; + *msg_size = 6 + payload_length + 2; /* header + payload + checksum */ + + /* check for complete message in buffer */ + if(*msg_size <= buff_size) { + /* Validate checksum of message */ + ck_a_rcv = serial_buff[*msg_size-2]; /* received checksum */ + ck_b_rcv = serial_buff[*msg_size-1]; /* received checksum */ + /* Use 8-bit Fletcher Algorithm to compute checksum of actual payload */ + ck_a = 0; ck_b = 0; + for (i=0; i<(4 + payload_length); i++) { + ck_a = ck_a + serial_buff[i+2]; + ck_b = ck_b + ck_a; + } + + /* Compare checksums and parse if OK */ + if ((ck_a == ck_a_rcv) && (ck_b == ck_b_rcv)) { + /* Check for Class 0x01 (NAV) and ID 0x20 (NAV-TIMEGPS) */ + if ((serial_buff[2] == 0x01) && (serial_buff[3] == 0x20)) { + /* Check validity of information */ + valid = serial_buff[17] & 0x3; /* towValid, weekValid */ + if (valid) { + /* Parse buffer to extract GPS time */ + /* Warning: payload byte ordering is Little Endian */ + gps_iTOW = (uint8_t)serial_buff[6]; + gps_iTOW |= (uint8_t)serial_buff[7] << 8; + gps_iTOW |= (uint8_t)serial_buff[8] << 16; + gps_iTOW |= (uint8_t)serial_buff[9] << 24; /* GPS time of week, in ms */ + + gps_fTOW = (uint8_t)serial_buff[10]; + gps_fTOW |= (uint8_t)serial_buff[11] << 8; + gps_fTOW |= (uint8_t)serial_buff[12] << 16; + gps_fTOW |= (uint8_t)serial_buff[13] << 24; /* Fractional part of iTOW, in ns */ + + gps_week = (uint8_t)serial_buff[14]; + gps_week |= (uint8_t)serial_buff[15] << 8; /* GPS week number */ + + gps_time_ok = true; +#if 0 + /* For debug */ + { + short ubx_gps_hou = 0; /* hours (0-23) */ + short ubx_gps_min = 0; /* minutes (0-59) */ + short ubx_gps_sec = 0; /* seconds (0-59) */ + + /* Format GPS time in hh:mm:ss based on iTOW */ + ubx_gps_sec = (gps_iTOW / 1000) % 60; + ubx_gps_min = (gps_iTOW / 1000 / 60) % 60; + ubx_gps_hou = (gps_iTOW / 1000 / 60 / 60) % 24; + printf(" GPS time = %02d:%02d:%02d\n", ubx_gps_hou, ubx_gps_min, ubx_gps_sec); + } +#endif + } else { /* valid */ + gps_time_ok = false; + } + + return UBX_NAV_TIMEGPS; + } else if ((serial_buff[2] == 0x05) && (serial_buff[3] == 0x00)) { + DEBUG_MSG("NOTE: UBX ACK-NAK received\n"); + return IGNORED; + } else if ((serial_buff[2] == 0x05) && (serial_buff[3] == 0x01)) { + DEBUG_MSG("NOTE: UBX ACK-ACK received\n"); + return IGNORED; + } else { /* not a supported message */ + DEBUG_MSG("ERROR: UBX message is not supported (%02x %02x)\n", serial_buff[2], serial_buff[3]); + return IGNORED; + } + } else { /* checksum failed */ + DEBUG_MSG("ERROR: UBX message is corrupted, checksum failed\n"); + return INVALID; + } + } else { /* message contains less bytes than indicated by header */ + DEBUG_MSG("ERROR: UBX message incomplete\n"); + return INCOMPLETE; + } + } else { /* Not a UBX message */ + /* Ignore messages which are not UBX ones for now */ + return IGNORED; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +enum gps_msg lgw_parse_nmea(const char *serial_buff, int buff_size) { + int i, j, k; + int str_index[30]; /* string index from the string chopping */ + int nb_fields; /* number of strings detected by string chopping */ + char parser_buf[256]; /* parsing modifies buffer so need a local copy */ + + /* check input parameters */ + if (serial_buff == NULL) { + return UNKNOWN; + } + + if(buff_size > (int)(sizeof(parser_buf) - 1)) { + DEBUG_MSG("Note: input string to big for parsing\n"); + return INVALID; + } + + /* look for some NMEA sentences in particular */ + if (buff_size < 8) { + DEBUG_MSG("ERROR: TOO SHORT TO BE A VALID NMEA SENTENCE\n"); + return UNKNOWN; + } else if (!validate_nmea_checksum(serial_buff, buff_size)) { + DEBUG_MSG("Warning: invalid NMEA sentence (bad checksum)\n"); + return INVALID; + } else if (match_label(serial_buff, "$G?RMC", 6, '?')) { + /* + NMEA sentence format: $xxRMC,time,status,lat,NS,long,EW,spd,cog,date,mv,mvEW,posMode*cs<CR><LF> + Valid fix: $GPRMC,083559.34,A,4717.11437,N,00833.91522,E,0.004,77.52,091202,,,A*00 + No fix: $GPRMC,,V,,,,,,,,,,N*00 + */ + memcpy(parser_buf, serial_buff, buff_size); + parser_buf[buff_size] = '\0'; + nb_fields = str_chop(parser_buf, buff_size, ',', str_index, ARRAY_SIZE(str_index)); + if (nb_fields != 13) { + DEBUG_MSG("Warning: invalid RMC sentence (number of fields)\n"); + return IGNORED; + } + /* parse GPS status */ + gps_mod = *(parser_buf + str_index[12]); /* get first character, no need to bother with sscanf */ + if ((gps_mod != 'N') && (gps_mod != 'A') && (gps_mod != 'D')) { + gps_mod = 'N'; + } + /* parse complete time */ + i = sscanf(parser_buf + str_index[1], "%2hd%2hd%2hd%4f", &gps_hou, &gps_min, &gps_sec, &gps_fra); + j = sscanf(parser_buf + str_index[9], "%2hd%2hd%2hd", &gps_day, &gps_mon, &gps_yea); + if ((i == 4) && (j == 3)) { + if ((gps_mod == 'A') || (gps_mod == 'D')) { + gps_time_ok = true; + DEBUG_MSG("Note: Valid RMC sentence, GPS locked, date: 20%02d-%02d-%02dT%02d:%02d:%06.3fZ\n", gps_yea, gps_mon, gps_day, gps_hou, gps_min, gps_fra + (float)gps_sec); + } else { + gps_time_ok = false; + DEBUG_MSG("Note: Valid RMC sentence, no satellite fix, estimated date: 20%02d-%02d-%02dT%02d:%02d:%06.3fZ\n", gps_yea, gps_mon, gps_day, gps_hou, gps_min, gps_fra + (float)gps_sec); + } + } else { + /* could not get a valid hour AND date */ + gps_time_ok = false; + DEBUG_MSG("Note: Valid RMC sentence, mode %c, no date\n", gps_mod); + } + return NMEA_RMC; + } else if (match_label(serial_buff, "$G?GGA", 6, '?')) { + /* + NMEA sentence format: $xxGGA,time,lat,NS,long,EW,quality,numSV,HDOP,alt,M,sep,M,diffAge,diffStation*cs<CR><LF> + Valid fix: $GPGGA,092725.00,4717.11399,N,00833.91590,E,1,08,1.01,499.6,M,48.0,M,,*5B + */ + memcpy(parser_buf, serial_buff, buff_size); + parser_buf[buff_size] = '\0'; + nb_fields = str_chop(parser_buf, buff_size, ',', str_index, ARRAY_SIZE(str_index)); + if (nb_fields != 15) { + DEBUG_MSG("Warning: invalid GGA sentence (number of fields)\n"); + return IGNORED; + } + /* parse number of satellites used for fix */ + sscanf(parser_buf + str_index[7], "%hd", &gps_sat); + /* parse 3D coordinates */ + i = sscanf(parser_buf + str_index[2], "%2hd%10lf", &gps_dla, &gps_mla); + gps_ola = *(parser_buf + str_index[3]); + j = sscanf(parser_buf + str_index[4], "%3hd%10lf", &gps_dlo, &gps_mlo); + gps_olo = *(parser_buf + str_index[5]); + k = sscanf(parser_buf + str_index[9], "%hd", &gps_alt); + if ((i == 2) && (j == 2) && (k == 1) && ((gps_ola=='N')||(gps_ola=='S')) && ((gps_olo=='E')||(gps_olo=='W'))) { + gps_pos_ok = true; + DEBUG_MSG("Note: Valid GGA sentence, %d sat, lat %02ddeg %06.3fmin %c, lon %03ddeg%06.3fmin %c, alt %d\n", gps_sat, gps_dla, gps_mla, gps_ola, gps_dlo, gps_mlo, gps_olo, gps_alt); + } else { + /* could not get a valid latitude, longitude AND altitude */ + gps_pos_ok = false; + DEBUG_MSG("Note: Valid GGA sentence, %d sat, no coordinates\n", gps_sat); + } + return NMEA_GGA; + } else { + DEBUG_MSG("Note: ignored NMEA sentence\n"); /* quite verbose */ + return IGNORED; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_gps_get(struct timespec *utc, struct timespec *gps_time, struct coord_s *loc, struct coord_s *err) { + struct tm x; + time_t y; + double intpart, fractpart; + + if (utc != NULL) { + if (!gps_time_ok) { + DEBUG_MSG("ERROR: NO VALID TIME TO RETURN\n"); + return LGW_GPS_ERROR; + } + memset(&x, 0, sizeof(x)); + if (gps_yea < 100) { /* 2-digits year, 20xx */ + x.tm_year = gps_yea + 100; /* 100 years offset to 1900 */ + } else { /* 4-digits year, Gregorian calendar */ + x.tm_year = gps_yea - 1900; + } + x.tm_mon = gps_mon - 1; /* tm_mon is [0,11], gps_mon is [1,12] */ + x.tm_mday = gps_day; + x.tm_hour = gps_hou; + x.tm_min = gps_min; + x.tm_sec = gps_sec; + y = mktime(&x) - timezone; /* need to substract timezone bc mktime assumes time vector is local time */ + if (y == (time_t)(-1)) { + DEBUG_MSG("ERROR: FAILED TO CONVERT BROKEN-DOWN TIME\n"); + return LGW_GPS_ERROR; + } + utc->tv_sec = y; + utc->tv_nsec = (int32_t)(gps_fra * 1e9); + } + if (gps_time != NULL) { + if (!gps_time_ok) { + DEBUG_MSG("ERROR: NO VALID TIME TO RETURN\n"); + return LGW_GPS_ERROR; + } + fractpart = modf(((double)gps_iTOW / 1E3) + ((double)gps_fTOW / 1E9), &intpart); + /* Number of seconds since beginning on current GPS week */ + gps_time->tv_sec = (time_t)intpart; + /* Number of seconds since GPS epoch 06.Jan.1980 */ + gps_time->tv_sec += (time_t)gps_week * 604800; /* day*hours*minutes*secondes: 7*24*60*60; */ + /* Fractional part in nanoseconds */ + gps_time->tv_nsec = (long)(fractpart * 1E9); + } + if (loc != NULL) { + if (!gps_pos_ok) { + DEBUG_MSG("ERROR: NO VALID POSITION TO RETURN\n"); + return LGW_GPS_ERROR; + } + loc->lat = ((double)gps_dla + (gps_mla/60.0)) * ((gps_ola == 'N')?1.0:-1.0); + loc->lon = ((double)gps_dlo + (gps_mlo/60.0)) * ((gps_olo == 'E')?1.0:-1.0); + loc->alt = gps_alt; + } + if (err != NULL) { + DEBUG_MSG("Warning: localization error processing not implemented yet\n"); + err->lat = 0.0; + err->lon = 0.0; + err->alt = 0; + } + + return LGW_GPS_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_gps_sync(struct tref *ref, uint32_t count_us, struct timespec utc, struct timespec gps_time) { + double cnt_diff; /* internal concentrator time difference (in seconds) */ + double utc_diff; /* UTC time difference (in seconds) */ + double slope; /* time slope between new reference and old reference (for sanity check) */ + + bool aber_n0; /* is the update value for synchronization aberrant or not ? */ + static bool aber_min1 = false; /* keep track of whether value at sync N-1 was aberrant or not */ + static bool aber_min2 = false; /* keep track of whether value at sync N-2 was aberrant or not */ + + CHECK_NULL(ref); + + /* calculate the slope */ + + cnt_diff = (double)(count_us - ref->count_us) / (double)(TS_CPS); /* uncorrected by xtal_err */ + utc_diff = (double)(utc.tv_sec - (ref->utc).tv_sec) + (1E-9 * (double)(utc.tv_nsec - (ref->utc).tv_nsec)); + + /* detect aberrant points by measuring if slope limits are exceeded */ + if (utc_diff != 0) { // prevent divide by zero + slope = cnt_diff/utc_diff; + if ((slope > PLUS_10PPM) || (slope < MINUS_10PPM)) { + DEBUG_MSG("Warning: correction range exceeded\n"); + aber_n0 = true; + } else { + aber_n0 = false; + } + } else { + DEBUG_MSG("Warning: aberrant UTC value for synchronization\n"); + aber_n0 = true; + } + + /* watch if the 3 latest sync point were aberrant or not */ + if (aber_n0 == false) { + /* value no aberrant -> sync with smoothed slope */ + ref->systime = time(NULL); + ref->count_us = count_us; + ref->utc.tv_sec = utc.tv_sec; + ref->utc.tv_nsec = utc.tv_nsec; + ref->gps.tv_sec = gps_time.tv_sec; + ref->gps.tv_nsec = gps_time.tv_nsec; + ref->xtal_err = slope; + aber_min2 = aber_min1; + aber_min1 = aber_n0; + return LGW_GPS_SUCCESS; + } else if (aber_n0 && aber_min1 && aber_min2) { + /* 3 successive aberrant values -> sync reset (keep xtal_err) */ + ref->systime = time(NULL); + ref->count_us = count_us; + ref->utc.tv_sec = utc.tv_sec; + ref->utc.tv_nsec = utc.tv_nsec; + ref->gps.tv_sec = gps_time.tv_sec; + ref->gps.tv_nsec = gps_time.tv_nsec; + /* reset xtal_err only if the present value is out of range */ + if ((ref->xtal_err > PLUS_10PPM) || (ref->xtal_err < MINUS_10PPM)) { + ref->xtal_err = 1.0; + } + DEBUG_MSG("Warning: 3 successive aberrant sync attempts, sync reset\n"); + aber_min2 = aber_min1; + aber_min1 = aber_n0; + return LGW_GPS_SUCCESS; + } else { + /* only 1 or 2 successive aberrant values -> ignore and return an error */ + aber_min2 = aber_min1; + aber_min1 = aber_n0; + return LGW_GPS_ERROR; + } + + return LGW_GPS_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_cnt2utc(struct tref ref, uint32_t count_us, struct timespec *utc) { + double delta_sec; + double intpart, fractpart; + long tmp; + + CHECK_NULL(utc); + if ((ref.systime == 0) || (ref.xtal_err > PLUS_10PPM) || (ref.xtal_err < MINUS_10PPM)) { + DEBUG_MSG("ERROR: INVALID REFERENCE FOR CNT -> UTC CONVERSION\n"); + return LGW_GPS_ERROR; + } + + /* calculate delta in seconds between reference count_us and target count_us */ + delta_sec = (double)(count_us - ref.count_us) / (TS_CPS * ref.xtal_err); + + /* now add that delta to reference UTC time */ + fractpart = modf (delta_sec , &intpart); + tmp = ref.utc.tv_nsec + (long)(fractpart * 1E9); + if (tmp < (long)1E9) { /* the nanosecond part doesn't overflow */ + utc->tv_sec = ref.utc.tv_sec + (time_t)intpart; + utc->tv_nsec = tmp; + } else { /* must carry one second */ + utc->tv_sec = ref.utc.tv_sec + (time_t)intpart + 1; + utc->tv_nsec = tmp - (long)1E9; + } + + return LGW_GPS_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_utc2cnt(struct tref ref, struct timespec utc, uint32_t *count_us) { + double delta_sec; + + CHECK_NULL(count_us); + if ((ref.systime == 0) || (ref.xtal_err > PLUS_10PPM) || (ref.xtal_err < MINUS_10PPM)) { + DEBUG_MSG("ERROR: INVALID REFERENCE FOR UTC -> CNT CONVERSION\n"); + return LGW_GPS_ERROR; + } + + /* calculate delta in seconds between reference utc and target utc */ + delta_sec = (double)(utc.tv_sec - ref.utc.tv_sec); + delta_sec += 1E-9 * (double)(utc.tv_nsec - ref.utc.tv_nsec); + + /* now convert that to internal counter tics and add that to reference counter value */ + *count_us = ref.count_us + (uint32_t)(delta_sec * TS_CPS * ref.xtal_err); + + return LGW_GPS_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_cnt2gps(struct tref ref, uint32_t count_us, struct timespec *gps_time) { + double delta_sec; + double intpart, fractpart; + long tmp; + + CHECK_NULL(gps_time); + if ((ref.systime == 0) || (ref.xtal_err > PLUS_10PPM) || (ref.xtal_err < MINUS_10PPM)) { + DEBUG_MSG("ERROR: INVALID REFERENCE FOR CNT -> GPS CONVERSION\n"); + return LGW_GPS_ERROR; + } + + /* calculate delta in milliseconds between reference count_us and target count_us */ + delta_sec = (double)(count_us - ref.count_us) / (TS_CPS * ref.xtal_err); + + /* now add that delta to reference GPS time */ + fractpart = modf (delta_sec , &intpart); + tmp = ref.gps.tv_nsec + (long)(fractpart * 1E9); + if (tmp < (long)1E9) { /* the nanosecond part doesn't overflow */ + gps_time->tv_sec = ref.gps.tv_sec + (time_t)intpart; + gps_time->tv_nsec = tmp; + } else { /* must carry one second */ + gps_time->tv_sec = ref.gps.tv_sec + (time_t)intpart + 1; + gps_time->tv_nsec = tmp - (long)1E9; + } + + return LGW_GPS_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_gps2cnt(struct tref ref, struct timespec gps_time, uint32_t *count_us) { + double delta_sec; + + CHECK_NULL(count_us); + if ((ref.systime == 0) || (ref.xtal_err > PLUS_10PPM) || (ref.xtal_err < MINUS_10PPM)) { + DEBUG_MSG("ERROR: INVALID REFERENCE FOR GPS -> CNT CONVERSION\n"); + return LGW_GPS_ERROR; + } + + /* calculate delta in seconds between reference gps time and target gps time */ + delta_sec = (double)(gps_time.tv_sec - ref.gps.tv_sec); + delta_sec += 1E-9 * (double)(gps_time.tv_nsec - ref.gps.tv_nsec); + + /* now convert that to internal counter tics and add that to reference counter value */ + *count_us = ref.count_us + (uint32_t)(delta_sec * TS_CPS * ref.xtal_err); + + return LGW_GPS_SUCCESS; +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/src/loragw_hal.c b/libloragw/src/loragw_hal.c new file mode 100644 index 0000000..8103751 --- /dev/null +++ b/libloragw/src/loragw_hal.c @@ -0,0 +1,1767 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + LoRa concentrator Hardware Abstraction Layer + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ +#include <stdio.h> /* printf fprintf */ +#include <string.h> /* memcpy */ +#include <math.h> /* pow, cell */ + +#include "loragw_reg.h" +#include "loragw_hal.h" +#include "loragw_aux.h" +#include "loragw_spi.h" +#include "loragw_radio.h" +#include "loragw_fpga.h" +#include "loragw_lbt.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#if DEBUG_HAL == 1 + #define DEBUG_MSG(str) fprintf(stderr, str) + #define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args) + #define DEBUG_ARRAY(a,b,c) for(a=0;a<b;++a) fprintf(stderr,"%x.",c[a]);fprintf(stderr,"end\n") + #define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_HAL_ERROR;} +#else + #define DEBUG_MSG(str) + #define DEBUG_PRINTF(fmt, args...) + #define DEBUG_ARRAY(a,b,c) for(a=0;a!=0;){} + #define CHECK_NULL(a) if(a==NULL){return LGW_HAL_ERROR;} +#endif + +#define IF_HZ_TO_REG(f) (f << 5)/15625 +#define SET_PPM_ON(bw,dr) (((bw == BW_125KHZ) && ((dr == DR_LORA_SF11) || (dr == DR_LORA_SF12))) || ((bw == BW_250KHZ) && (dr == DR_LORA_SF12))) +#define TRACE() fprintf(stderr, "@ %s %d\n", __FUNCTION__, __LINE__); + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS & TYPES -------------------------------------------- */ + +#define MCU_ARB 0 +#define MCU_AGC 1 +#define MCU_ARB_FW_BYTE 8192 /* size of the firmware IN BYTES (= twice the number of 14b words) */ +#define MCU_AGC_FW_BYTE 8192 /* size of the firmware IN BYTES (= twice the number of 14b words) */ +#define FW_VERSION_ADDR 0x20 /* Address of firmware version in data memory */ +#define FW_VERSION_CAL 2 /* Expected version of calibration firmware */ +#define FW_VERSION_AGC 4 /* Expected version of AGC firmware */ +#define FW_VERSION_ARB 1 /* Expected version of arbiter firmware */ + +#define TX_METADATA_NB 16 +#define RX_METADATA_NB 16 + +#define AGC_CMD_WAIT 16 +#define AGC_CMD_ABORT 17 + +#define MIN_LORA_PREAMBLE 6 +#define STD_LORA_PREAMBLE 8 +#define MIN_FSK_PREAMBLE 3 +#define STD_FSK_PREAMBLE 5 + +#define RSSI_MULTI_BIAS -35 /* difference between "multi" modem RSSI offset and "stand-alone" modem RSSI offset */ +#define RSSI_FSK_POLY_0 60 /* polynomiam coefficients to linearize FSK RSSI */ +#define RSSI_FSK_POLY_1 1.5351 +#define RSSI_FSK_POLY_2 0.003 + +/* Useful bandwidth of SX125x radios to consider depending on channel bandwidth */ +/* Note: the below values come from lab measurements. For any question, please contact Semtech support */ +#define LGW_RF_RX_BANDWIDTH_125KHZ 925000 /* for 125KHz channels */ +#define LGW_RF_RX_BANDWIDTH_250KHZ 1000000 /* for 250KHz channels */ +#define LGW_RF_RX_BANDWIDTH_500KHZ 1100000 /* for 500KHz channels */ + +#define TX_START_DELAY_DEFAULT 1497 /* Calibrated value for 500KHz BW and notch filter disabled */ + +/* constant arrays defining hardware capability */ +const uint8_t ifmod_config[LGW_IF_CHAIN_NB] = LGW_IFMODEM_CONFIG; + +/* Version string, used to identify the library version/options once compiled */ +const char lgw_version_string[] = "Version: " LIBLORAGW_VERSION ";"; + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE VARIABLES ---------------------------------------------------- */ + +#include "arb_fw.var" /* external definition of the variable */ +#include "agc_fw.var" /* external definition of the variable */ +#include "cal_fw.var" /* external definition of the variable */ + +/* +The following static variables are the configuration set that the user can +modify using rxrf_setconf, rxif_setconf and txgain_setconf functions. +The functions _start and _send then use that set to configure the hardware. + +Parameters validity and coherency is verified by the _setconf functions and +the _start and _send functions assume they are valid. +*/ + +static bool lgw_is_started; + +static bool rf_enable[LGW_RF_CHAIN_NB]; +static uint32_t rf_rx_freq[LGW_RF_CHAIN_NB]; /* absolute, in Hz */ +static float rf_rssi_offset[LGW_RF_CHAIN_NB]; +static bool rf_tx_enable[LGW_RF_CHAIN_NB]; +static uint32_t rf_tx_notch_freq[LGW_RF_CHAIN_NB]; +static enum lgw_radio_type_e rf_radio_type[LGW_RF_CHAIN_NB]; + +static bool if_enable[LGW_IF_CHAIN_NB]; +static bool if_rf_chain[LGW_IF_CHAIN_NB]; /* for each IF, 0 -> radio A, 1 -> radio B */ +static int32_t if_freq[LGW_IF_CHAIN_NB]; /* relative to radio frequency, +/- in Hz */ + +static uint8_t lora_multi_sfmask[LGW_MULTI_NB]; /* enables SF for LoRa 'multi' modems */ + +static uint8_t lora_rx_bw; /* bandwidth setting for LoRa standalone modem */ +static uint8_t lora_rx_sf; /* spreading factor setting for LoRa standalone modem */ +static bool lora_rx_ppm_offset; + +static uint8_t fsk_rx_bw; /* bandwidth setting of FSK modem */ +static uint32_t fsk_rx_dr; /* FSK modem datarate in bauds */ +static uint8_t fsk_sync_word_size = 3; /* default number of bytes for FSK sync word */ +static uint64_t fsk_sync_word= 0xC194C1; /* default FSK sync word (ALIGNED RIGHT, MSbit first) */ + +static bool lorawan_public = false; +static uint8_t rf_clkout = 0; + +static struct lgw_tx_gain_lut_s txgain_lut = { + .size = 2, + .lut[0] = { + .dig_gain = 0, + .pa_gain = 2, + .dac_gain = 3, + .mix_gain = 10, + .rf_power = 14 + }, + .lut[1] = { + .dig_gain = 0, + .pa_gain = 3, + .dac_gain = 3, + .mix_gain = 14, + .rf_power = 27 + }}; + +/* TX I/Q imbalance coefficients for mixer gain = 8 to 15 */ +static int8_t cal_offset_a_i[8]; /* TX I offset for radio A */ +static int8_t cal_offset_a_q[8]; /* TX Q offset for radio A */ +static int8_t cal_offset_b_i[8]; /* TX I offset for radio B */ +static int8_t cal_offset_b_q[8]; /* TX Q offset for radio B */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ + +int load_firmware(uint8_t target, uint8_t *firmware, uint16_t size); + +void lgw_constant_adjust(void); + +int32_t lgw_sf_getval(int x); +int32_t lgw_bw_getval(int x); + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ + +/* size is the firmware size in bytes (not 14b words) */ +int load_firmware(uint8_t target, uint8_t *firmware, uint16_t size) { + int reg_rst; + int reg_sel; + uint8_t fw_check[8192]; + int32_t dummy; + + /* check parameters */ + CHECK_NULL(firmware); + if (target == MCU_ARB) { + if (size != MCU_ARB_FW_BYTE) { + DEBUG_MSG("ERROR: NOT A VALID SIZE FOR MCU ARG FIRMWARE\n"); + return -1; + } + reg_rst = LGW_MCU_RST_0; + reg_sel = LGW_MCU_SELECT_MUX_0; + }else if (target == MCU_AGC) { + if (size != MCU_AGC_FW_BYTE) { + DEBUG_MSG("ERROR: NOT A VALID SIZE FOR MCU AGC FIRMWARE\n"); + return -1; + } + reg_rst = LGW_MCU_RST_1; + reg_sel = LGW_MCU_SELECT_MUX_1; + } else { + DEBUG_MSG("ERROR: NOT A VALID TARGET FOR LOADING FIRMWARE\n"); + return -1; + } + + /* reset the targeted MCU */ + lgw_reg_w(reg_rst, 1); + + /* set mux to access MCU program RAM and set address to 0 */ + lgw_reg_w(reg_sel, 0); + lgw_reg_w(LGW_MCU_PROM_ADDR, 0); + + /* write the program in one burst */ + lgw_reg_wb(LGW_MCU_PROM_DATA, firmware, size); + + /* Read back firmware code for check */ + lgw_reg_r( LGW_MCU_PROM_DATA, &dummy ); /* bug workaround */ + lgw_reg_rb( LGW_MCU_PROM_DATA, fw_check, size ); + if (memcmp(firmware, fw_check, size) != 0) { + printf ("ERROR: Failed to load fw %d\n", (int)target); + return -1; + } + + /* give back control of the MCU program ram to the MCU */ + lgw_reg_w(reg_sel, 1); + + return 0; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +void lgw_constant_adjust(void) { + + /* I/Q path setup */ + // lgw_reg_w(LGW_RX_INVERT_IQ,0); /* default 0 */ + // lgw_reg_w(LGW_MODEM_INVERT_IQ,1); /* default 1 */ + // lgw_reg_w(LGW_CHIRP_INVERT_RX,1); /* default 1 */ + // lgw_reg_w(LGW_RX_EDGE_SELECT,0); /* default 0 */ + // lgw_reg_w(LGW_MBWSSF_MODEM_INVERT_IQ,0); /* default 0 */ + // lgw_reg_w(LGW_DC_NOTCH_EN,1); /* default 1 */ + lgw_reg_w(LGW_RSSI_BB_FILTER_ALPHA,6); /* default 7 */ + lgw_reg_w(LGW_RSSI_DEC_FILTER_ALPHA,7); /* default 5 */ + lgw_reg_w(LGW_RSSI_CHANN_FILTER_ALPHA,7); /* default 8 */ + lgw_reg_w(LGW_RSSI_BB_DEFAULT_VALUE,23); /* default 32 */ + lgw_reg_w(LGW_RSSI_CHANN_DEFAULT_VALUE,85); /* default 100 */ + lgw_reg_w(LGW_RSSI_DEC_DEFAULT_VALUE,66); /* default 100 */ + lgw_reg_w(LGW_DEC_GAIN_OFFSET,7); /* default 8 */ + lgw_reg_w(LGW_CHAN_GAIN_OFFSET,6); /* default 7 */ + + /* Correlator setup */ + // lgw_reg_w(LGW_CORR_DETECT_EN,126); /* default 126 */ + // lgw_reg_w(LGW_CORR_NUM_SAME_PEAK,4); /* default 4 */ + // lgw_reg_w(LGW_CORR_MAC_GAIN,5); /* default 5 */ + // lgw_reg_w(LGW_CORR_SAME_PEAKS_OPTION_SF6,0); /* default 0 */ + // lgw_reg_w(LGW_CORR_SAME_PEAKS_OPTION_SF7,1); /* default 1 */ + // lgw_reg_w(LGW_CORR_SAME_PEAKS_OPTION_SF8,1); /* default 1 */ + // lgw_reg_w(LGW_CORR_SAME_PEAKS_OPTION_SF9,1); /* default 1 */ + // lgw_reg_w(LGW_CORR_SAME_PEAKS_OPTION_SF10,1); /* default 1 */ + // lgw_reg_w(LGW_CORR_SAME_PEAKS_OPTION_SF11,1); /* default 1 */ + // lgw_reg_w(LGW_CORR_SAME_PEAKS_OPTION_SF12,1); /* default 1 */ + // lgw_reg_w(LGW_CORR_SIG_NOISE_RATIO_SF6,4); /* default 4 */ + // lgw_reg_w(LGW_CORR_SIG_NOISE_RATIO_SF7,4); /* default 4 */ + // lgw_reg_w(LGW_CORR_SIG_NOISE_RATIO_SF8,4); /* default 4 */ + // lgw_reg_w(LGW_CORR_SIG_NOISE_RATIO_SF9,4); /* default 4 */ + // lgw_reg_w(LGW_CORR_SIG_NOISE_RATIO_SF10,4); /* default 4 */ + // lgw_reg_w(LGW_CORR_SIG_NOISE_RATIO_SF11,4); /* default 4 */ + // lgw_reg_w(LGW_CORR_SIG_NOISE_RATIO_SF12,4); /* default 4 */ + + /* LoRa 'multi' demodulators setup */ + // lgw_reg_w(LGW_PREAMBLE_SYMB1_NB,10); /* default 10 */ + // lgw_reg_w(LGW_FREQ_TO_TIME_INVERT,29); /* default 29 */ + // lgw_reg_w(LGW_FRAME_SYNCH_GAIN,1); /* default 1 */ + // lgw_reg_w(LGW_SYNCH_DETECT_TH,1); /* default 1 */ + // lgw_reg_w(LGW_ZERO_PAD,0); /* default 0 */ + lgw_reg_w(LGW_SNR_AVG_CST,3); /* default 2 */ + if (lorawan_public) { /* LoRa network */ + lgw_reg_w(LGW_FRAME_SYNCH_PEAK1_POS,3); /* default 1 */ + lgw_reg_w(LGW_FRAME_SYNCH_PEAK2_POS,4); /* default 2 */ + } else { /* private network */ + lgw_reg_w(LGW_FRAME_SYNCH_PEAK1_POS,1); /* default 1 */ + lgw_reg_w(LGW_FRAME_SYNCH_PEAK2_POS,2); /* default 2 */ + } + + // lgw_reg_w(LGW_PREAMBLE_FINE_TIMING_GAIN,1); /* default 1 */ + // lgw_reg_w(LGW_ONLY_CRC_EN,1); /* default 1 */ + // lgw_reg_w(LGW_PAYLOAD_FINE_TIMING_GAIN,2); /* default 2 */ + // lgw_reg_w(LGW_TRACKING_INTEGRAL,0); /* default 0 */ + // lgw_reg_w(LGW_ADJUST_MODEM_START_OFFSET_RDX8,0); /* default 0 */ + // lgw_reg_w(LGW_ADJUST_MODEM_START_OFFSET_SF12_RDX4,4092); /* default 4092 */ + // lgw_reg_w(LGW_MAX_PAYLOAD_LEN,255); /* default 255 */ + + /* LoRa standalone 'MBWSSF' demodulator setup */ + // lgw_reg_w(LGW_MBWSSF_PREAMBLE_SYMB1_NB,10); /* default 10 */ + // lgw_reg_w(LGW_MBWSSF_FREQ_TO_TIME_INVERT,29); /* default 29 */ + // lgw_reg_w(LGW_MBWSSF_FRAME_SYNCH_GAIN,1); /* default 1 */ + // lgw_reg_w(LGW_MBWSSF_SYNCH_DETECT_TH,1); /* default 1 */ + // lgw_reg_w(LGW_MBWSSF_ZERO_PAD,0); /* default 0 */ + if (lorawan_public) { /* LoRa network */ + lgw_reg_w(LGW_MBWSSF_FRAME_SYNCH_PEAK1_POS,3); /* default 1 */ + lgw_reg_w(LGW_MBWSSF_FRAME_SYNCH_PEAK2_POS,4); /* default 2 */ + } else { + lgw_reg_w(LGW_MBWSSF_FRAME_SYNCH_PEAK1_POS,1); /* default 1 */ + lgw_reg_w(LGW_MBWSSF_FRAME_SYNCH_PEAK2_POS,2); /* default 2 */ + } + // lgw_reg_w(LGW_MBWSSF_ONLY_CRC_EN,1); /* default 1 */ + // lgw_reg_w(LGW_MBWSSF_PAYLOAD_FINE_TIMING_GAIN,2); /* default 2 */ + // lgw_reg_w(LGW_MBWSSF_PREAMBLE_FINE_TIMING_GAIN,1); /* default 1 */ + // lgw_reg_w(LGW_MBWSSF_TRACKING_INTEGRAL,0); /* default 0 */ + // lgw_reg_w(LGW_MBWSSF_AGC_FREEZE_ON_DETECT,1); /* default 1 */ + + /* Improvement of reference clock frequency error tolerance */ + lgw_reg_w(LGW_ADJUST_MODEM_START_OFFSET_RDX4, 1); /* default 0 */ + lgw_reg_w(LGW_ADJUST_MODEM_START_OFFSET_SF12_RDX4, 4094); /* default 4092 */ + lgw_reg_w(LGW_CORR_MAC_GAIN, 7); /* default 5 */ + + /* FSK datapath setup */ + lgw_reg_w(LGW_FSK_RX_INVERT,1); /* default 0 */ + lgw_reg_w(LGW_FSK_MODEM_INVERT_IQ,1); /* default 0 */ + + /* FSK demodulator setup */ + lgw_reg_w(LGW_FSK_RSSI_LENGTH,4); /* default 0 */ + lgw_reg_w(LGW_FSK_PKT_MODE,1); /* variable length, default 0 */ + lgw_reg_w(LGW_FSK_CRC_EN,1); /* default 0 */ + lgw_reg_w(LGW_FSK_DCFREE_ENC,2); /* default 0 */ + // lgw_reg_w(LGW_FSK_CRC_IBM,0); /* default 0 */ + lgw_reg_w(LGW_FSK_ERROR_OSR_TOL,10); /* default 0 */ + lgw_reg_w(LGW_FSK_PKT_LENGTH,255); /* max packet length in variable length mode */ + // lgw_reg_w(LGW_FSK_NODE_ADRS,0); /* default 0 */ + // lgw_reg_w(LGW_FSK_BROADCAST,0); /* default 0 */ + // lgw_reg_w(LGW_FSK_AUTO_AFC_ON,0); /* default 0 */ + lgw_reg_w(LGW_FSK_PATTERN_TIMEOUT_CFG,128); /* sync timeout (allow 8 bytes preamble + 8 bytes sync word, default 0 */ + + /* TX general parameters */ + lgw_reg_w(LGW_TX_START_DELAY, TX_START_DELAY_DEFAULT); /* default 0 */ + + /* TX LoRa */ + // lgw_reg_w(LGW_TX_MODE,0); /* default 0 */ + lgw_reg_w(LGW_TX_SWAP_IQ,1); /* "normal" polarity; default 0 */ + if (lorawan_public) { /* LoRa network */ + lgw_reg_w(LGW_TX_FRAME_SYNCH_PEAK1_POS,3); /* default 1 */ + lgw_reg_w(LGW_TX_FRAME_SYNCH_PEAK2_POS,4); /* default 2 */ + } else { /* Private network */ + lgw_reg_w(LGW_TX_FRAME_SYNCH_PEAK1_POS,1); /* default 1 */ + lgw_reg_w(LGW_TX_FRAME_SYNCH_PEAK2_POS,2); /* default 2 */ + } + + /* TX FSK */ + // lgw_reg_w(LGW_FSK_TX_GAUSSIAN_EN,1); /* default 1 */ + lgw_reg_w(LGW_FSK_TX_GAUSSIAN_SELECT_BT,2); /* Gaussian filter always on TX, default 0 */ + // lgw_reg_w(LGW_FSK_TX_PATTERN_EN,1); /* default 1 */ + // lgw_reg_w(LGW_FSK_TX_PREAMBLE_SEQ,0); /* default 0 */ + + return; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int32_t lgw_bw_getval(int x) { + switch (x) { + case BW_500KHZ: return 500000; + case BW_250KHZ: return 250000; + case BW_125KHZ: return 125000; + case BW_62K5HZ: return 62500; + case BW_31K2HZ: return 31200; + case BW_15K6HZ: return 15600; + case BW_7K8HZ : return 7800; + default: return -1; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int32_t lgw_sf_getval(int x) { + switch (x) { + case DR_LORA_SF7: return 7; + case DR_LORA_SF8: return 8; + case DR_LORA_SF9: return 9; + case DR_LORA_SF10: return 10; + case DR_LORA_SF11: return 11; + case DR_LORA_SF12: return 12; + default: return -1; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +uint16_t lgw_get_tx_start_delay(bool tx_notch_enable, uint8_t bw) { + float notch_delay_us = 0.0; + float bw_delay_us = 0.0; + float tx_start_delay; + + /* Notch filtering performed by FPGA adds a constant delay (group delay) that we need to compensate */ + if (tx_notch_enable) { + notch_delay_us = lgw_fpga_get_tx_notch_delay(); + } + + /* Calibrated delay brought by SX1301 depending on signal bandwidth */ + switch (bw) { + case BW_125KHZ: + bw_delay_us = 1.5; + break; + case BW_500KHZ: + /* Intended fall-through: it is the calibrated reference */ + default: + break; + } + + tx_start_delay = (float)TX_START_DELAY_DEFAULT - bw_delay_us - notch_delay_us; + + printf("INFO: tx_start_delay=%u (%f) - (%u, bw_delay=%f, notch_delay=%f)\n", (uint16_t)tx_start_delay, tx_start_delay, TX_START_DELAY_DEFAULT, bw_delay_us, notch_delay_us); + + return (uint16_t)tx_start_delay; /* keep truncating instead of rounding: better behaviour measured */ +} + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ + +int lgw_board_setconf(struct lgw_conf_board_s conf) { + + /* check if the concentrator is running */ + if (lgw_is_started == true) { + DEBUG_MSG("ERROR: CONCENTRATOR IS RUNNING, STOP IT BEFORE TOUCHING CONFIGURATION\n"); + return LGW_HAL_ERROR; + } + + /* set internal config according to parameters */ + lorawan_public = conf.lorawan_public; + rf_clkout = conf.clksrc; + + DEBUG_PRINTF("Note: board configuration; lorawan_public:%d, clksrc:%d\n", lorawan_public, rf_clkout); + + return LGW_HAL_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_lbt_setconf(struct lgw_conf_lbt_s conf) { + int x; + + /* check if the concentrator is running */ + if (lgw_is_started == true) { + DEBUG_MSG("ERROR: CONCENTRATOR IS RUNNING, STOP IT BEFORE TOUCHING CONFIGURATION\n"); + return LGW_HAL_ERROR; + } + + x = lbt_setconf(&conf); + if (x != LGW_LBT_SUCCESS) { + DEBUG_MSG("ERROR: Failed to configure concentrator for LBT\n"); + return LGW_HAL_ERROR; + } + + return LGW_HAL_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_rxrf_setconf(uint8_t rf_chain, struct lgw_conf_rxrf_s conf) { + + /* check if the concentrator is running */ + if (lgw_is_started == true) { + DEBUG_MSG("ERROR: CONCENTRATOR IS RUNNING, STOP IT BEFORE TOUCHING CONFIGURATION\n"); + return LGW_HAL_ERROR; + } + + /* check input range (segfault prevention) */ + if (rf_chain >= LGW_RF_CHAIN_NB) { + DEBUG_MSG("ERROR: NOT A VALID RF_CHAIN NUMBER\n"); + return LGW_HAL_ERROR; + } + + /* check if radio type is supported */ + if ((conf.type != LGW_RADIO_TYPE_SX1255) && (conf.type != LGW_RADIO_TYPE_SX1257)) { + DEBUG_MSG("ERROR: NOT A VALID RADIO TYPE\n"); + return LGW_HAL_ERROR; + } + + /* check if TX notch filter frequency is supported */ + if ((conf.tx_enable == true) && ((conf.tx_notch_freq < LGW_MIN_NOTCH_FREQ) || (conf.tx_notch_freq > LGW_MAX_NOTCH_FREQ))) { + DEBUG_PRINTF("WARNING: NOT A VALID TX NOTCH FILTER FREQUENCY [%u..%u]Hz\n", LGW_MIN_NOTCH_FREQ, LGW_MAX_NOTCH_FREQ); + conf.tx_notch_freq = 0; + } + + /* set internal config according to parameters */ + rf_enable[rf_chain] = conf.enable; + rf_rx_freq[rf_chain] = conf.freq_hz; + rf_rssi_offset[rf_chain] = conf.rssi_offset; + rf_radio_type[rf_chain] = conf.type; + rf_tx_enable[rf_chain] = conf.tx_enable; + rf_tx_notch_freq[rf_chain] = conf.tx_notch_freq; + + DEBUG_PRINTF("Note: rf_chain %d configuration; en:%d freq:%d rssi_offset:%f radio_type:%d tx_enable:%d tx_notch_freq:%u\n", rf_chain, rf_enable[rf_chain], rf_rx_freq[rf_chain], rf_rssi_offset[rf_chain], rf_radio_type[rf_chain], rf_tx_enable[rf_chain], rf_tx_notch_freq[rf_chain]); + + return LGW_HAL_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_rxif_setconf(uint8_t if_chain, struct lgw_conf_rxif_s conf) { + int32_t bw_hz; + uint32_t rf_rx_bandwidth; + + /* check if the concentrator is running */ + if (lgw_is_started == true) { + DEBUG_MSG("ERROR: CONCENTRATOR IS RUNNING, STOP IT BEFORE TOUCHING CONFIGURATION\n"); + return LGW_HAL_ERROR; + } + + /* check input range (segfault prevention) */ + if (if_chain >= LGW_IF_CHAIN_NB) { + DEBUG_PRINTF("ERROR: %d NOT A VALID IF_CHAIN NUMBER\n", if_chain); + return LGW_HAL_ERROR; + } + + /* if chain is disabled, don't care about most parameters */ + if (conf.enable == false) { + if_enable[if_chain] = false; + if_freq[if_chain] = 0; + DEBUG_PRINTF("Note: if_chain %d disabled\n", if_chain); + return LGW_HAL_SUCCESS; + } + + /* check 'general' parameters */ + if (ifmod_config[if_chain] == IF_UNDEFINED) { + DEBUG_PRINTF("ERROR: IF CHAIN %d NOT CONFIGURABLE\n", if_chain); + } + if (conf.rf_chain >= LGW_RF_CHAIN_NB) { + DEBUG_MSG("ERROR: INVALID RF_CHAIN TO ASSOCIATE WITH A LORA_STD IF CHAIN\n"); + return LGW_HAL_ERROR; + } + /* check if IF frequency is optimal based on channel and radio bandwidths */ + switch (conf.bandwidth) { + case BW_250KHZ: + rf_rx_bandwidth = LGW_RF_RX_BANDWIDTH_250KHZ; /* radio bandwidth */ + break; + case BW_500KHZ: + rf_rx_bandwidth = LGW_RF_RX_BANDWIDTH_500KHZ; /* radio bandwidth */ + break; + default: + /* For 125KHz and below */ + rf_rx_bandwidth = LGW_RF_RX_BANDWIDTH_125KHZ; /* radio bandwidth */ + break; + } + bw_hz = lgw_bw_getval(conf.bandwidth); /* channel bandwidth */ + if ((conf.freq_hz + ((bw_hz==-1)?LGW_REF_BW:bw_hz)/2) > ((int32_t)rf_rx_bandwidth/2)) { + DEBUG_PRINTF("ERROR: IF FREQUENCY %d TOO HIGH\n", conf.freq_hz); + return LGW_HAL_ERROR; + } else if ((conf.freq_hz - ((bw_hz==-1)?LGW_REF_BW:bw_hz)/2) < -((int32_t)rf_rx_bandwidth/2)) { + DEBUG_PRINTF("ERROR: IF FREQUENCY %d TOO LOW\n", conf.freq_hz); + return LGW_HAL_ERROR; + } + + /* check parameters according to the type of IF chain + modem, + fill default if necessary, and commit configuration if everything is OK */ + switch (ifmod_config[if_chain]) { + case IF_LORA_STD: + /* fill default parameters if needed */ + if (conf.bandwidth == BW_UNDEFINED) { + conf.bandwidth = BW_250KHZ; + } + if (conf.datarate == DR_UNDEFINED) { + conf.datarate = DR_LORA_SF9; + } + /* check BW & DR */ + if (!IS_LORA_BW(conf.bandwidth)) { + DEBUG_MSG("ERROR: BANDWIDTH NOT SUPPORTED BY LORA_STD IF CHAIN\n"); + return LGW_HAL_ERROR; + } + if (!IS_LORA_STD_DR(conf.datarate)) { + DEBUG_MSG("ERROR: DATARATE NOT SUPPORTED BY LORA_STD IF CHAIN\n"); + return LGW_HAL_ERROR; + } + /* set internal configuration */ + if_enable[if_chain] = conf.enable; + if_rf_chain[if_chain] = conf.rf_chain; + if_freq[if_chain] = conf.freq_hz; + lora_rx_bw = conf.bandwidth; + lora_rx_sf = (uint8_t)(DR_LORA_MULTI & conf.datarate); /* filter SF out of the 7-12 range */ + if (SET_PPM_ON(conf.bandwidth, conf.datarate)) { + lora_rx_ppm_offset = true; + } else { + lora_rx_ppm_offset = false; + } + + DEBUG_PRINTF("Note: LoRa 'std' if_chain %d configuration; en:%d freq:%d bw:%d dr:%d\n", if_chain, if_enable[if_chain], if_freq[if_chain], lora_rx_bw, lora_rx_sf); + break; + + case IF_LORA_MULTI: + /* fill default parameters if needed */ + if (conf.bandwidth == BW_UNDEFINED) { + conf.bandwidth = BW_125KHZ; + } + if (conf.datarate == DR_UNDEFINED) { + conf.datarate = DR_LORA_MULTI; + } + /* check BW & DR */ + if (conf.bandwidth != BW_125KHZ) { + DEBUG_MSG("ERROR: BANDWIDTH NOT SUPPORTED BY LORA_MULTI IF CHAIN\n"); + return LGW_HAL_ERROR; + } + if (!IS_LORA_MULTI_DR(conf.datarate)) { + DEBUG_MSG("ERROR: DATARATE(S) NOT SUPPORTED BY LORA_MULTI IF CHAIN\n"); + return LGW_HAL_ERROR; + } + /* set internal configuration */ + if_enable[if_chain] = conf.enable; + if_rf_chain[if_chain] = conf.rf_chain; + if_freq[if_chain] = conf.freq_hz; + lora_multi_sfmask[if_chain] = (uint8_t)(DR_LORA_MULTI & conf.datarate); /* filter SF out of the 7-12 range */ + + DEBUG_PRINTF("Note: LoRa 'multi' if_chain %d configuration; en:%d freq:%d SF_mask:0x%02x\n", if_chain, if_enable[if_chain], if_freq[if_chain], lora_multi_sfmask[if_chain]); + break; + + case IF_FSK_STD: + /* fill default parameters if needed */ + if (conf.bandwidth == BW_UNDEFINED) { + conf.bandwidth = BW_250KHZ; + } + if (conf.datarate == DR_UNDEFINED) { + conf.datarate = 64000; /* default datarate */ + } + /* check BW & DR */ + if(!IS_FSK_BW(conf.bandwidth)) { + DEBUG_MSG("ERROR: BANDWIDTH NOT SUPPORTED BY FSK IF CHAIN\n"); + return LGW_HAL_ERROR; + } + if(!IS_FSK_DR(conf.datarate)) { + DEBUG_MSG("ERROR: DATARATE NOT SUPPORTED BY FSK IF CHAIN\n"); + return LGW_HAL_ERROR; + } + /* set internal configuration */ + if_enable[if_chain] = conf.enable; + if_rf_chain[if_chain] = conf.rf_chain; + if_freq[if_chain] = conf.freq_hz; + fsk_rx_bw = conf.bandwidth; + fsk_rx_dr = conf.datarate; + if (conf.sync_word > 0) { + fsk_sync_word_size = conf.sync_word_size; + fsk_sync_word = conf.sync_word; + } + DEBUG_PRINTF("Note: FSK if_chain %d configuration; en:%d freq:%d bw:%d dr:%d (%d real dr) sync:0x%0*llX\n", if_chain, if_enable[if_chain], if_freq[if_chain], fsk_rx_bw, fsk_rx_dr, LGW_XTAL_FREQU/(LGW_XTAL_FREQU/fsk_rx_dr), 2*fsk_sync_word_size, fsk_sync_word); + break; + + default: + DEBUG_PRINTF("ERROR: IF CHAIN %d TYPE NOT SUPPORTED\n", if_chain); + return LGW_HAL_ERROR; + } + + return LGW_HAL_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_txgain_setconf(struct lgw_tx_gain_lut_s *conf) { + int i; + + /* Check LUT size */ + if ((conf->size < 1) || (conf->size > TX_GAIN_LUT_SIZE_MAX)) { + DEBUG_PRINTF("ERROR: TX gain LUT must have at least one entry and maximum %d entries\n", TX_GAIN_LUT_SIZE_MAX); + return LGW_HAL_ERROR; + } + + txgain_lut.size = conf->size; + + for (i = 0; i < txgain_lut.size; i++) { + /* Check gain range */ + if (conf->lut[i].dig_gain > 3) { + DEBUG_MSG("ERROR: TX gain LUT: SX1301 digital gain must be between 0 and 3\n"); + return LGW_HAL_ERROR; + } + if (conf->lut[i].dac_gain != 3) { + DEBUG_MSG("ERROR: TX gain LUT: SX1257 DAC gains != 3 are not supported\n"); + return LGW_HAL_ERROR; + } + if (conf->lut[i].mix_gain > 15) { + DEBUG_MSG("ERROR: TX gain LUT: SX1257 mixer gain must not exceed 15\n"); + return LGW_HAL_ERROR; + } else if (conf->lut[i].mix_gain < 8) { + DEBUG_MSG("ERROR: TX gain LUT: SX1257 mixer gains < 8 are not supported\n"); + return LGW_HAL_ERROR; + } + if (conf->lut[i].pa_gain > 3) { + DEBUG_MSG("ERROR: TX gain LUT: External PA gain must not exceed 3\n"); + return LGW_HAL_ERROR; + } + + /* Set internal LUT */ + txgain_lut.lut[i].dig_gain = conf->lut[i].dig_gain; + txgain_lut.lut[i].dac_gain = conf->lut[i].dac_gain; + txgain_lut.lut[i].mix_gain = conf->lut[i].mix_gain; + txgain_lut.lut[i].pa_gain = conf->lut[i].pa_gain; + txgain_lut.lut[i].rf_power = conf->lut[i].rf_power; + } + + return LGW_HAL_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_start(void) { + int i, err; + int reg_stat; + unsigned x; + uint8_t radio_select; + int32_t read_val; + uint8_t load_val; + uint8_t fw_version; + uint8_t cal_cmd; + uint16_t cal_time; + uint8_t cal_status; + + uint64_t fsk_sync_word_reg; + + if (lgw_is_started == true) { + DEBUG_MSG("Note: LoRa concentrator already started, restarting it now\n"); + } + + reg_stat = lgw_connect(false, rf_tx_notch_freq[rf_tx_enable[1]?1:0]); + if (reg_stat == LGW_REG_ERROR) { + DEBUG_MSG("ERROR: FAIL TO CONNECT BOARD\n"); + return LGW_HAL_ERROR; + } + + /* reset the registers (also shuts the radios down) */ + lgw_soft_reset(); + + /* gate clocks */ + lgw_reg_w(LGW_GLOBAL_EN, 0); + lgw_reg_w(LGW_CLK32M_EN, 0); + + /* switch on and reset the radios (also starts the 32 MHz XTAL) */ + lgw_reg_w(LGW_RADIO_A_EN,1); + lgw_reg_w(LGW_RADIO_B_EN,1); + wait_ms(500); /* TODO: optimize */ + lgw_reg_w(LGW_RADIO_RST,1); + wait_ms(5); + lgw_reg_w(LGW_RADIO_RST,0); + + /* setup the radios */ + err = lgw_setup_sx125x(0, rf_clkout, rf_enable[0], rf_radio_type[0], rf_rx_freq[0]); + if (err != 0) { + DEBUG_MSG("ERROR: Failed to setup sx125x radio for RF chain 0\n"); + return LGW_HAL_ERROR; + } + err = lgw_setup_sx125x(1, rf_clkout, rf_enable[1], rf_radio_type[1], rf_rx_freq[1]); + if (err != 0) { + DEBUG_MSG("ERROR: Failed to setup sx125x radio for RF chain 0\n"); + return LGW_HAL_ERROR; + } + + /* gives AGC control of GPIOs to enable Tx external digital filter */ + lgw_reg_w(LGW_GPIO_MODE,31); /* Set all GPIOs as output */ + lgw_reg_w(LGW_GPIO_SELECT_OUTPUT,2); + + /* Configure LBT */ + if (lbt_is_enabled() == true) { + lgw_reg_w(LGW_CLK32M_EN, 1); + i = lbt_setup(); + if (i != LGW_LBT_SUCCESS) { + DEBUG_MSG("ERROR: lbt_setup() did not return SUCCESS\n"); + return LGW_HAL_ERROR; + } + + /* Start SX1301 counter and LBT FSM at the same time to be in sync */ + lgw_reg_w(LGW_CLK32M_EN, 0); + i = lbt_start(); + if (i != LGW_LBT_SUCCESS) { + DEBUG_MSG("ERROR: lbt_start() did not return SUCCESS\n"); + return LGW_HAL_ERROR; + } + } + + /* Enable clocks */ + lgw_reg_w(LGW_GLOBAL_EN, 1); + lgw_reg_w(LGW_CLK32M_EN, 1); + + /* GPIOs table : + DGPIO0 -> N/A + DGPIO1 -> N/A + DGPIO2 -> N/A + DGPIO3 -> TX digital filter ON + DGPIO4 -> TX ON + */ + + /* select calibration command */ + cal_cmd = 0; + cal_cmd |= rf_enable[0] ? 0x01 : 0x00; /* Bit 0: Calibrate Rx IQ mismatch compensation on radio A */ + cal_cmd |= rf_enable[1] ? 0x02 : 0x00; /* Bit 1: Calibrate Rx IQ mismatch compensation on radio B */ + cal_cmd |= (rf_enable[0] && rf_tx_enable[0]) ? 0x04 : 0x00; /* Bit 2: Calibrate Tx DC offset on radio A */ + cal_cmd |= (rf_enable[1] && rf_tx_enable[1]) ? 0x08 : 0x00; /* Bit 3: Calibrate Tx DC offset on radio B */ + cal_cmd |= 0x10; /* Bit 4: 0: calibrate with DAC gain=2, 1: with DAC gain=3 (use 3) */ + + switch (rf_radio_type[0]) { /* we assume that there is only one radio type on the board */ + case LGW_RADIO_TYPE_SX1255: + cal_cmd |= 0x20; /* Bit 5: 0: SX1257, 1: SX1255 */ + break; + case LGW_RADIO_TYPE_SX1257: + cal_cmd |= 0x00; /* Bit 5: 0: SX1257, 1: SX1255 */ + break; + default: + DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d FOR RADIO TYPE\n", rf_radio_type[0]); + break; + } + + cal_cmd |= 0x00; /* Bit 6-7: Board type 0: ref, 1: FPGA, 3: board X */ + cal_time = 2300; /* measured between 2.1 and 2.2 sec, because 1 TX only */ + + /* Load the calibration firmware */ + load_firmware(MCU_AGC, cal_firmware, MCU_AGC_FW_BYTE); + lgw_reg_w(LGW_FORCE_HOST_RADIO_CTRL, 0); /* gives to AGC MCU the control of the radios */ + lgw_reg_w(LGW_RADIO_SELECT, cal_cmd); /* send calibration configuration word */ + lgw_reg_w(LGW_MCU_RST_1, 0); + + /* Check firmware version */ + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, FW_VERSION_ADDR); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + fw_version = (uint8_t)read_val; + if (fw_version != FW_VERSION_CAL) { + printf("ERROR: Version of calibration firmware not expected, actual:%d expected:%d\n", fw_version, FW_VERSION_CAL); + return -1; + } + + lgw_reg_w(LGW_PAGE_REG, 3); /* Calibration will start on this condition as soon as MCU can talk to concentrator registers */ + lgw_reg_w(LGW_EMERGENCY_FORCE_HOST_CTRL, 0); /* Give control of concentrator registers to MCU */ + + /* Wait for calibration to end */ + DEBUG_PRINTF("Note: calibration started (time: %u ms)\n", cal_time); + wait_ms(cal_time); /* Wait for end of calibration */ + lgw_reg_w(LGW_EMERGENCY_FORCE_HOST_CTRL, 1); /* Take back control */ + + /* Get calibration status */ + lgw_reg_r(LGW_MCU_AGC_STATUS, &read_val); + cal_status = (uint8_t)read_val; + /* + bit 7: calibration finished + bit 0: could access SX1301 registers + bit 1: could access radio A registers + bit 2: could access radio B registers + bit 3: radio A RX image rejection successful + bit 4: radio B RX image rejection successful + bit 5: radio A TX DC Offset correction successful + bit 6: radio B TX DC Offset correction successful + */ + if ((cal_status & 0x81) != 0x81) { + DEBUG_PRINTF("ERROR: CALIBRATION FAILURE (STATUS = %u)\n", cal_status); + return LGW_HAL_ERROR; + } else { + DEBUG_PRINTF("Note: calibration finished (status = %u)\n", cal_status); + } + if (rf_enable[0] && ((cal_status & 0x02) == 0)) { + DEBUG_MSG("WARNING: calibration could not access radio A\n"); + } + if (rf_enable[1] && ((cal_status & 0x04) == 0)) { + DEBUG_MSG("WARNING: calibration could not access radio B\n"); + } + if (rf_enable[0] && ((cal_status & 0x08) == 0)) { + DEBUG_MSG("WARNING: problem in calibration of radio A for image rejection\n"); + } + if (rf_enable[1] && ((cal_status & 0x10) == 0)) { + DEBUG_MSG("WARNING: problem in calibration of radio B for image rejection\n"); + } + if (rf_enable[0] && rf_tx_enable[0] && ((cal_status & 0x20) == 0)) { + DEBUG_MSG("WARNING: problem in calibration of radio A for TX DC offset\n"); + } + if (rf_enable[1] && rf_tx_enable[1] && ((cal_status & 0x40) == 0)) { + DEBUG_MSG("WARNING: problem in calibration of radio B for TX DC offset\n"); + } + + /* Get TX DC offset values */ + for(i=0; i<=7; ++i) { + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xA0+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + cal_offset_a_i[i] = (int8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xA8+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + cal_offset_a_q[i] = (int8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xB0+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + cal_offset_b_i[i] = (int8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xB8+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + cal_offset_b_q[i] = (int8_t)read_val; + } + + /* load adjusted parameters */ + lgw_constant_adjust(); + + /* Sanity check for RX frequency */ + if (rf_rx_freq[0] == 0) { + DEBUG_MSG("ERROR: wrong configuration, rf_rx_freq[0] is not set\n"); + return LGW_HAL_ERROR; + } + + /* Freq-to-time-drift calculation */ + x = 4096000000 / (rf_rx_freq[0] >> 1); /* dividend: (4*2048*1000000) >> 1, rescaled to avoid 32b overflow */ + x = ( x > 63 ) ? 63 : x; /* saturation */ + lgw_reg_w(LGW_FREQ_TO_TIME_DRIFT, x); /* default 9 */ + + x = 4096000000 / (rf_rx_freq[0] >> 3); /* dividend: (16*2048*1000000) >> 3, rescaled to avoid 32b overflow */ + x = ( x > 63 ) ? 63 : x; /* saturation */ + lgw_reg_w(LGW_MBWSSF_FREQ_TO_TIME_DRIFT, x); /* default 36 */ + + /* configure LoRa 'multi' demodulators aka. LoRa 'sensor' channels (IF0-3) */ + radio_select = 0; /* IF mapping to radio A/B (per bit, 0=A, 1=B) */ + for(i=0; i<LGW_MULTI_NB; ++i) { + radio_select += (if_rf_chain[i] == 1 ? 1 << i : 0); /* transform bool array into binary word */ + } + /* + lgw_reg_w(LGW_RADIO_SELECT, radio_select); + + LGW_RADIO_SELECT is used for communication with the firmware, "radio_select" + will be loaded in LGW_RADIO_SELECT at the end of start procedure. + */ + + lgw_reg_w(LGW_IF_FREQ_0, IF_HZ_TO_REG(if_freq[0])); /* default -384 */ + lgw_reg_w(LGW_IF_FREQ_1, IF_HZ_TO_REG(if_freq[1])); /* default -128 */ + lgw_reg_w(LGW_IF_FREQ_2, IF_HZ_TO_REG(if_freq[2])); /* default 128 */ + lgw_reg_w(LGW_IF_FREQ_3, IF_HZ_TO_REG(if_freq[3])); /* default 384 */ + lgw_reg_w(LGW_IF_FREQ_4, IF_HZ_TO_REG(if_freq[4])); /* default -384 */ + lgw_reg_w(LGW_IF_FREQ_5, IF_HZ_TO_REG(if_freq[5])); /* default -128 */ + lgw_reg_w(LGW_IF_FREQ_6, IF_HZ_TO_REG(if_freq[6])); /* default 128 */ + lgw_reg_w(LGW_IF_FREQ_7, IF_HZ_TO_REG(if_freq[7])); /* default 384 */ + + lgw_reg_w(LGW_CORR0_DETECT_EN, (if_enable[0] == true) ? lora_multi_sfmask[0] : 0); /* default 0 */ + lgw_reg_w(LGW_CORR1_DETECT_EN, (if_enable[1] == true) ? lora_multi_sfmask[1] : 0); /* default 0 */ + lgw_reg_w(LGW_CORR2_DETECT_EN, (if_enable[2] == true) ? lora_multi_sfmask[2] : 0); /* default 0 */ + lgw_reg_w(LGW_CORR3_DETECT_EN, (if_enable[3] == true) ? lora_multi_sfmask[3] : 0); /* default 0 */ + lgw_reg_w(LGW_CORR4_DETECT_EN, (if_enable[4] == true) ? lora_multi_sfmask[4] : 0); /* default 0 */ + lgw_reg_w(LGW_CORR5_DETECT_EN, (if_enable[5] == true) ? lora_multi_sfmask[5] : 0); /* default 0 */ + lgw_reg_w(LGW_CORR6_DETECT_EN, (if_enable[6] == true) ? lora_multi_sfmask[6] : 0); /* default 0 */ + lgw_reg_w(LGW_CORR7_DETECT_EN, (if_enable[7] == true) ? lora_multi_sfmask[7] : 0); /* default 0 */ + + lgw_reg_w(LGW_PPM_OFFSET, 0x60); /* as the threshold is 16ms, use 0x60 to enable ppm_offset for SF12 and SF11 @125kHz*/ + + lgw_reg_w(LGW_CONCENTRATOR_MODEM_ENABLE, 1); /* default 0 */ + + /* configure LoRa 'stand-alone' modem (IF8) */ + lgw_reg_w(LGW_IF_FREQ_8, IF_HZ_TO_REG(if_freq[8])); /* MBWSSF modem (default 0) */ + if (if_enable[8] == true) { + lgw_reg_w(LGW_MBWSSF_RADIO_SELECT, if_rf_chain[8]); + switch(lora_rx_bw) { + case BW_125KHZ: lgw_reg_w(LGW_MBWSSF_MODEM_BW, 0); break; + case BW_250KHZ: lgw_reg_w(LGW_MBWSSF_MODEM_BW, 1); break; + case BW_500KHZ: lgw_reg_w(LGW_MBWSSF_MODEM_BW, 2); break; + default: + DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", lora_rx_bw); + return LGW_HAL_ERROR; + } + switch(lora_rx_sf) { + case DR_LORA_SF7: lgw_reg_w(LGW_MBWSSF_RATE_SF, 7); break; + case DR_LORA_SF8: lgw_reg_w(LGW_MBWSSF_RATE_SF, 8); break; + case DR_LORA_SF9: lgw_reg_w(LGW_MBWSSF_RATE_SF, 9); break; + case DR_LORA_SF10: lgw_reg_w(LGW_MBWSSF_RATE_SF, 10); break; + case DR_LORA_SF11: lgw_reg_w(LGW_MBWSSF_RATE_SF, 11); break; + case DR_LORA_SF12: lgw_reg_w(LGW_MBWSSF_RATE_SF, 12); break; + default: + DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", lora_rx_sf); + return LGW_HAL_ERROR; + } + lgw_reg_w(LGW_MBWSSF_PPM_OFFSET, lora_rx_ppm_offset); /* default 0 */ + lgw_reg_w(LGW_MBWSSF_MODEM_ENABLE, 1); /* default 0 */ + } else { + lgw_reg_w(LGW_MBWSSF_MODEM_ENABLE, 0); + } + + /* configure FSK modem (IF9) */ + lgw_reg_w(LGW_IF_FREQ_9, IF_HZ_TO_REG(if_freq[9])); /* FSK modem, default 0 */ + lgw_reg_w(LGW_FSK_PSIZE, fsk_sync_word_size-1); + lgw_reg_w(LGW_FSK_TX_PSIZE, fsk_sync_word_size-1); + fsk_sync_word_reg = fsk_sync_word << (8 * (8 - fsk_sync_word_size)); + lgw_reg_w(LGW_FSK_REF_PATTERN_LSB, (uint32_t)(0xFFFFFFFF & fsk_sync_word_reg)); + lgw_reg_w(LGW_FSK_REF_PATTERN_MSB, (uint32_t)(0xFFFFFFFF & (fsk_sync_word_reg >> 32))); + if (if_enable[9] == true) { + lgw_reg_w(LGW_FSK_RADIO_SELECT, if_rf_chain[9]); + lgw_reg_w(LGW_FSK_BR_RATIO, LGW_XTAL_FREQU/fsk_rx_dr); /* setting the dividing ratio for datarate */ + lgw_reg_w(LGW_FSK_CH_BW_EXPO, fsk_rx_bw); + lgw_reg_w(LGW_FSK_MODEM_ENABLE, 1); /* default 0 */ + } else { + lgw_reg_w(LGW_FSK_MODEM_ENABLE, 0); + } + + /* Load firmware */ + load_firmware(MCU_ARB, arb_firmware, MCU_ARB_FW_BYTE); + load_firmware(MCU_AGC, agc_firmware, MCU_AGC_FW_BYTE); + + /* gives the AGC MCU control over radio, RF front-end and filter gain */ + lgw_reg_w(LGW_FORCE_HOST_RADIO_CTRL, 0); + lgw_reg_w(LGW_FORCE_HOST_FE_CTRL, 0); + lgw_reg_w(LGW_FORCE_DEC_FILTER_GAIN, 0); + + /* Get MCUs out of reset */ + lgw_reg_w(LGW_RADIO_SELECT, 0); /* MUST not be = to 1 or 2 at firmware init */ + lgw_reg_w(LGW_MCU_RST_0, 0); + lgw_reg_w(LGW_MCU_RST_1, 0); + + /* Check firmware version */ + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, FW_VERSION_ADDR); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + fw_version = (uint8_t)read_val; + if (fw_version != FW_VERSION_AGC) { + DEBUG_PRINTF("ERROR: Version of AGC firmware not expected, actual:%d expected:%d\n", fw_version, FW_VERSION_AGC); + return LGW_HAL_ERROR; + } + lgw_reg_w(LGW_DBG_ARB_MCU_RAM_ADDR, FW_VERSION_ADDR); + lgw_reg_r(LGW_DBG_ARB_MCU_RAM_DATA, &read_val); + fw_version = (uint8_t)read_val; + if (fw_version != FW_VERSION_ARB) { + DEBUG_PRINTF("ERROR: Version of arbiter firmware not expected, actual:%d expected:%d\n", fw_version, FW_VERSION_ARB); + return LGW_HAL_ERROR; + } + + DEBUG_MSG("Info: Initialising AGC firmware...\n"); + wait_ms(1); + + lgw_reg_r(LGW_MCU_AGC_STATUS, &read_val); + if (read_val != 0x10) { + DEBUG_PRINTF("ERROR: AGC FIRMWARE INITIALIZATION FAILURE, STATUS 0x%02X\n", (uint8_t)read_val); + return LGW_HAL_ERROR; + } + + /* Update Tx gain LUT and start AGC */ + for (i = 0; i < txgain_lut.size; ++i) { + lgw_reg_w(LGW_RADIO_SELECT, AGC_CMD_WAIT); /* start a transaction */ + wait_ms(1); + load_val = txgain_lut.lut[i].mix_gain + (16 * txgain_lut.lut[i].dac_gain) + (64 * txgain_lut.lut[i].pa_gain); + lgw_reg_w(LGW_RADIO_SELECT, load_val); + wait_ms(1); + lgw_reg_r(LGW_MCU_AGC_STATUS, &read_val); + if (read_val != (0x30 + i)) { + DEBUG_PRINTF("ERROR: AGC FIRMWARE INITIALIZATION FAILURE, STATUS 0x%02X\n", (uint8_t)read_val); + return LGW_HAL_ERROR; + } + } + /* As the AGC fw is waiting for 16 entries, we need to abort the transaction if we get less entries */ + if (txgain_lut.size < TX_GAIN_LUT_SIZE_MAX) { + lgw_reg_w(LGW_RADIO_SELECT, AGC_CMD_WAIT); + wait_ms(1); + load_val = AGC_CMD_ABORT; + lgw_reg_w(LGW_RADIO_SELECT, load_val); + wait_ms(1); + lgw_reg_r(LGW_MCU_AGC_STATUS, &read_val); + if (read_val != 0x30) { + DEBUG_PRINTF("ERROR: AGC FIRMWARE INITIALIZATION FAILURE, STATUS 0x%02X\n", (uint8_t)read_val); + return LGW_HAL_ERROR; + } + } + + /* Load Tx freq MSBs (always 3 if f > 768 for SX1257 or f > 384 for SX1255 */ + lgw_reg_w(LGW_RADIO_SELECT, AGC_CMD_WAIT); + wait_ms(1); + lgw_reg_w(LGW_RADIO_SELECT, 3); + wait_ms(1); + lgw_reg_r(LGW_MCU_AGC_STATUS, &read_val); + if (read_val != 0x33) { + DEBUG_PRINTF("ERROR: AGC FIRMWARE INITIALIZATION FAILURE, STATUS 0x%02X\n", (uint8_t)read_val); + return LGW_HAL_ERROR; + } + + /* Load chan_select firmware option */ + lgw_reg_w(LGW_RADIO_SELECT, AGC_CMD_WAIT); + wait_ms(1); + lgw_reg_w(LGW_RADIO_SELECT, 0); + wait_ms(1); + lgw_reg_r(LGW_MCU_AGC_STATUS, &read_val); + if (read_val != 0x30) { + DEBUG_PRINTF("ERROR: AGC FIRMWARE INITIALIZATION FAILURE, STATUS 0x%02X\n", (uint8_t)read_val); + return LGW_HAL_ERROR; + } + + /* End AGC firmware init and check status */ + lgw_reg_w(LGW_RADIO_SELECT, AGC_CMD_WAIT); + wait_ms(1); + lgw_reg_w(LGW_RADIO_SELECT, radio_select); /* Load intended value of RADIO_SELECT */ + wait_ms(1); + DEBUG_MSG("Info: putting back original RADIO_SELECT value\n"); + lgw_reg_r(LGW_MCU_AGC_STATUS, &read_val); + if (read_val != 0x40) { + DEBUG_PRINTF("ERROR: AGC FIRMWARE INITIALIZATION FAILURE, STATUS 0x%02X\n", (uint8_t)read_val); + return LGW_HAL_ERROR; + } + + /* enable GPS event capture */ + lgw_reg_w(LGW_GPS_EN, 1); + + /* */ + if (lbt_is_enabled() == true) { + printf("INFO: Configuring LBT, this may take few seconds, please wait...\n"); + wait_ms(8400); + } + + lgw_is_started = true; + return LGW_HAL_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_stop(void) { + lgw_soft_reset(); + lgw_disconnect(); + + lgw_is_started = false; + return LGW_HAL_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_receive(uint8_t max_pkt, struct lgw_pkt_rx_s *pkt_data) { + int nb_pkt_fetch; /* loop variable and return value */ + struct lgw_pkt_rx_s *p; /* pointer to the current structure in the struct array */ + uint8_t buff[255+RX_METADATA_NB]; /* buffer to store the result of SPI read bursts */ + unsigned sz; /* size of the payload, uses to address metadata */ + int ifmod; /* type of if_chain/modem a packet was received by */ + int stat_fifo; /* the packet status as indicated in the FIFO */ + uint32_t raw_timestamp; /* timestamp when internal 'RX finished' was triggered */ + uint32_t delay_x, delay_y, delay_z; /* temporary variable for timestamp offset calculation */ + uint32_t timestamp_correction; /* correction to account for processing delay */ + uint32_t sf, cr, bw_pow, crc_en, ppm; /* used to calculate timestamp correction */ + + /* check if the concentrator is running */ + if (lgw_is_started == false) { + DEBUG_MSG("ERROR: CONCENTRATOR IS NOT RUNNING, START IT BEFORE RECEIVING\n"); + return LGW_HAL_ERROR; + } + + /* check input variables */ + if ((max_pkt <= 0) || (max_pkt > LGW_PKT_FIFO_SIZE)) { + DEBUG_PRINTF("ERROR: %d = INVALID MAX NUMBER OF PACKETS TO FETCH\n", max_pkt); + return LGW_HAL_ERROR; + } + CHECK_NULL(pkt_data); + + /* Initialize buffer */ + memset (buff, 0, sizeof buff); + + /* iterate max_pkt times at most */ + for (nb_pkt_fetch = 0; nb_pkt_fetch < max_pkt; ++nb_pkt_fetch) { + + /* point to the proper struct in the struct array */ + p = &pkt_data[nb_pkt_fetch]; + + /* fetch all the RX FIFO data */ + lgw_reg_rb(LGW_RX_PACKET_DATA_FIFO_NUM_STORED, buff, 5); + /* 0: number of packets available in RX data buffer */ + /* 1,2: start address of the current packet in RX data buffer */ + /* 3: CRC status of the current packet */ + /* 4: size of the current packet payload in byte */ + + /* how many packets are in the RX buffer ? Break if zero */ + if (buff[0] == 0) { + break; /* no more packets to fetch, exit out of FOR loop */ + } + + /* sanity check */ + if (buff[0] > LGW_PKT_FIFO_SIZE) { + DEBUG_PRINTF("WARNING: %u = INVALID NUMBER OF PACKETS TO FETCH, ABORTING\n", buff[0]); + break; + } + + DEBUG_PRINTF("FIFO content: %x %x %x %x %x\n", buff[0], buff[1], buff[2], buff[3], buff[4]); + + p->size = buff[4]; + sz = p->size; + stat_fifo = buff[3]; /* will be used later, need to save it before overwriting buff */ + + /* get payload + metadata */ + lgw_reg_rb(LGW_RX_DATA_BUF_DATA, buff, sz+RX_METADATA_NB); + + /* copy payload to result struct */ + memcpy((void *)p->payload, (void *)buff, sz); + + /* process metadata */ + p->if_chain = buff[sz+0]; + if (p->if_chain >= LGW_IF_CHAIN_NB) { + DEBUG_PRINTF("WARNING: %u NOT A VALID IF_CHAIN NUMBER, ABORTING\n", p->if_chain); + break; + } + ifmod = ifmod_config[p->if_chain]; + DEBUG_PRINTF("[%d %d]\n", p->if_chain, ifmod); + + p->rf_chain = (uint8_t)if_rf_chain[p->if_chain]; + p->freq_hz = (uint32_t)((int32_t)rf_rx_freq[p->rf_chain] + if_freq[p->if_chain]); + p->rssi = (float)buff[sz+5] + rf_rssi_offset[p->rf_chain]; + + if ((ifmod == IF_LORA_MULTI) || (ifmod == IF_LORA_STD)) { + DEBUG_MSG("Note: LoRa packet\n"); + switch(stat_fifo & 0x07) { + case 5: + p->status = STAT_CRC_OK; + crc_en = 1; + break; + case 7: + p->status = STAT_CRC_BAD; + crc_en = 1; + break; + case 1: + p->status = STAT_NO_CRC; + crc_en = 0; + break; + default: + p->status = STAT_UNDEFINED; + crc_en = 0; + } + p->modulation = MOD_LORA; + p->snr = ((float)((int8_t)buff[sz+2]))/4; + p->snr_min = ((float)((int8_t)buff[sz+3]))/4; + p->snr_max = ((float)((int8_t)buff[sz+4]))/4; + if (ifmod == IF_LORA_MULTI) { + p->bandwidth = BW_125KHZ; /* fixed in hardware */ + } else { + p->bandwidth = lora_rx_bw; /* get the parameter from the config variable */ + } + sf = (buff[sz+1] >> 4) & 0x0F; + switch (sf) { + case 7: p->datarate = DR_LORA_SF7; break; + case 8: p->datarate = DR_LORA_SF8; break; + case 9: p->datarate = DR_LORA_SF9; break; + case 10: p->datarate = DR_LORA_SF10; break; + case 11: p->datarate = DR_LORA_SF11; break; + case 12: p->datarate = DR_LORA_SF12; break; + default: p->datarate = DR_UNDEFINED; + } + cr = (buff[sz+1] >> 1) & 0x07; + switch (cr) { + case 1: p->coderate = CR_LORA_4_5; break; + case 2: p->coderate = CR_LORA_4_6; break; + case 3: p->coderate = CR_LORA_4_7; break; + case 4: p->coderate = CR_LORA_4_8; break; + default: p->coderate = CR_UNDEFINED; + } + + /* determine if 'PPM mode' is on, needed for timestamp correction */ + if (SET_PPM_ON(p->bandwidth,p->datarate)) { + ppm = 1; + } else { + ppm = 0; + } + + /* timestamp correction code, base delay */ + if (ifmod == IF_LORA_STD) { /* if packet was received on the stand-alone LoRa modem */ + switch (lora_rx_bw) { + case BW_125KHZ: + delay_x = 64; + bw_pow = 1; + break; + case BW_250KHZ: + delay_x = 32; + bw_pow = 2; + break; + case BW_500KHZ: + delay_x = 16; + bw_pow = 4; + break; + default: + DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", p->bandwidth); + delay_x = 0; + bw_pow = 0; + } + } else { /* packet was received on one of the sensor channels = 125kHz */ + delay_x = 114; + bw_pow = 1; + } + + /* timestamp correction code, variable delay */ + if ((sf >= 6) && (sf <= 12) && (bw_pow > 0)) { + if ((2*(sz + 2*crc_en) - (sf-7)) <= 0) { /* payload fits entirely in first 8 symbols */ + delay_y = ( ((1<<(sf-1)) * (sf+1)) + (3 * (1<<(sf-4))) ) / bw_pow; + delay_z = 32 * (2*(sz+2*crc_en) + 5) / bw_pow; + } else { + delay_y = ( ((1<<(sf-1)) * (sf+1)) + ((4 - ppm) * (1<<(sf-4))) ) / bw_pow; + delay_z = (16 + 4*cr) * (((2*(sz+2*crc_en)-sf+6) % (sf - 2*ppm)) + 1) / bw_pow; + } + timestamp_correction = delay_x + delay_y + delay_z; + } else { + timestamp_correction = 0; + DEBUG_MSG("WARNING: invalid packet, no timestamp correction\n"); + } + + /* RSSI correction */ + if (ifmod == IF_LORA_MULTI) { + p->rssi -= RSSI_MULTI_BIAS; + } + + } else if (ifmod == IF_FSK_STD) { + DEBUG_MSG("Note: FSK packet\n"); + switch(stat_fifo & 0x07) { + case 5: + p->status = STAT_CRC_OK; + break; + case 7: + p->status = STAT_CRC_BAD; + break; + case 1: + p->status = STAT_NO_CRC; + break; + default: + p->status = STAT_UNDEFINED; + break; + } + p->modulation = MOD_FSK; + p->snr = -128.0; + p->snr_min = -128.0; + p->snr_max = -128.0; + p->bandwidth = fsk_rx_bw; + p->datarate = fsk_rx_dr; + p->coderate = CR_UNDEFINED; + timestamp_correction = ((uint32_t)680000 / fsk_rx_dr) - 20; + + /* RSSI correction */ + p->rssi = RSSI_FSK_POLY_0 + RSSI_FSK_POLY_1 * p->rssi + RSSI_FSK_POLY_2 * pow(p->rssi, 2); + } else { + DEBUG_MSG("ERROR: UNEXPECTED PACKET ORIGIN\n"); + p->status = STAT_UNDEFINED; + p->modulation = MOD_UNDEFINED; + p->rssi = -128.0; + p->snr = -128.0; + p->snr_min = -128.0; + p->snr_max = -128.0; + p->bandwidth = BW_UNDEFINED; + p->datarate = DR_UNDEFINED; + p->coderate = CR_UNDEFINED; + timestamp_correction = 0; + } + + raw_timestamp = (uint32_t)buff[sz+6] + ((uint32_t)buff[sz+7] << 8) + ((uint32_t)buff[sz+8] << 16) + ((uint32_t)buff[sz+9] << 24); + p->count_us = raw_timestamp - timestamp_correction; + p->crc = (uint16_t)buff[sz+10] + ((uint16_t)buff[sz+11] << 8); + + /* advance packet FIFO */ + lgw_reg_w(LGW_RX_PACKET_DATA_FIFO_NUM_STORED, 0); + } + + return nb_pkt_fetch; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_send(struct lgw_pkt_tx_s pkt_data) { + int i, x; + uint8_t buff[256+TX_METADATA_NB]; /* buffer to prepare the packet to send + metadata before SPI write burst */ + uint32_t part_int = 0; /* integer part for PLL register value calculation */ + uint32_t part_frac = 0; /* fractional part for PLL register value calculation */ + uint16_t fsk_dr_div; /* divider to configure for target datarate */ + int transfer_size = 0; /* data to transfer from host to TX databuffer */ + int payload_offset = 0; /* start of the payload content in the databuffer */ + uint8_t pow_index = 0; /* 4-bit value to set the firmware TX power */ + uint8_t target_mix_gain = 0; /* used to select the proper I/Q offset correction */ + uint32_t count_trig = 0; /* timestamp value in trigger mode corrected for TX start delay */ + bool tx_allowed = false; + uint16_t tx_start_delay; + bool tx_notch_enable = false; + + /* check if the concentrator is running */ + if (lgw_is_started == false) { + DEBUG_MSG("ERROR: CONCENTRATOR IS NOT RUNNING, START IT BEFORE SENDING\n"); + return LGW_HAL_ERROR; + } + + /* check input range (segfault prevention) */ + if (pkt_data.rf_chain >= LGW_RF_CHAIN_NB) { + DEBUG_MSG("ERROR: INVALID RF_CHAIN TO SEND PACKETS\n"); + return LGW_HAL_ERROR; + } + + /* check input variables */ + if (rf_tx_enable[pkt_data.rf_chain] == false) { + DEBUG_MSG("ERROR: SELECTED RF_CHAIN IS DISABLED FOR TX ON SELECTED BOARD\n"); + return LGW_HAL_ERROR; + } + if (rf_enable[pkt_data.rf_chain] == false) { + DEBUG_MSG("ERROR: SELECTED RF_CHAIN IS DISABLED\n"); + return LGW_HAL_ERROR; + } + if (!IS_TX_MODE(pkt_data.tx_mode)) { + DEBUG_MSG("ERROR: TX_MODE NOT SUPPORTED\n"); + return LGW_HAL_ERROR; + } + if (pkt_data.modulation == MOD_LORA) { + if (!IS_LORA_BW(pkt_data.bandwidth)) { + DEBUG_MSG("ERROR: BANDWIDTH NOT SUPPORTED BY LORA TX\n"); + return LGW_HAL_ERROR; + } + if (!IS_LORA_STD_DR(pkt_data.datarate)) { + DEBUG_MSG("ERROR: DATARATE NOT SUPPORTED BY LORA TX\n"); + return LGW_HAL_ERROR; + } + if (!IS_LORA_CR(pkt_data.coderate)) { + DEBUG_MSG("ERROR: CODERATE NOT SUPPORTED BY LORA TX\n"); + return LGW_HAL_ERROR; + } + if (pkt_data.size > 255) { + DEBUG_MSG("ERROR: PAYLOAD LENGTH TOO BIG FOR LORA TX\n"); + return LGW_HAL_ERROR; + } + } else if (pkt_data.modulation == MOD_FSK) { + if((pkt_data.f_dev < 1) || (pkt_data.f_dev > 200)) { + DEBUG_MSG("ERROR: TX FREQUENCY DEVIATION OUT OF ACCEPTABLE RANGE\n"); + return LGW_HAL_ERROR; + } + if(!IS_FSK_DR(pkt_data.datarate)) { + DEBUG_MSG("ERROR: DATARATE NOT SUPPORTED BY FSK IF CHAIN\n"); + return LGW_HAL_ERROR; + } + if (pkt_data.size > 255) { + DEBUG_MSG("ERROR: PAYLOAD LENGTH TOO BIG FOR FSK TX\n"); + return LGW_HAL_ERROR; + } + } else { + DEBUG_MSG("ERROR: INVALID TX MODULATION\n"); + return LGW_HAL_ERROR; + } + + /* Enable notch filter for LoRa 125kHz */ + if ((pkt_data.modulation == MOD_LORA) && (pkt_data.bandwidth == BW_125KHZ)) { + tx_notch_enable = true; + } + + /* Get the TX start delay to be applied for this TX */ + tx_start_delay = lgw_get_tx_start_delay(tx_notch_enable, pkt_data.bandwidth); + + /* interpretation of TX power */ + for (pow_index = txgain_lut.size-1; pow_index > 0; pow_index--) { + if (txgain_lut.lut[pow_index].rf_power <= pkt_data.rf_power) { + break; + } + } + + /* loading TX imbalance correction */ + target_mix_gain = txgain_lut.lut[pow_index].mix_gain; + if (pkt_data.rf_chain == 0) { /* use radio A calibration table */ + lgw_reg_w(LGW_TX_OFFSET_I, cal_offset_a_i[target_mix_gain - 8]); + lgw_reg_w(LGW_TX_OFFSET_Q, cal_offset_a_q[target_mix_gain - 8]); + } else { /* use radio B calibration table */ + lgw_reg_w(LGW_TX_OFFSET_I, cal_offset_b_i[target_mix_gain - 8]); + lgw_reg_w(LGW_TX_OFFSET_Q, cal_offset_b_q[target_mix_gain - 8]); + } + + /* Set digital gain from LUT */ + lgw_reg_w(LGW_TX_GAIN, txgain_lut.lut[pow_index].dig_gain); + + /* fixed metadata, useful payload and misc metadata compositing */ + transfer_size = TX_METADATA_NB + pkt_data.size; /* */ + payload_offset = TX_METADATA_NB; /* start the payload just after the metadata */ + + /* metadata 0 to 2, TX PLL frequency */ + switch (rf_radio_type[0]) { /* we assume that there is only one radio type on the board */ + case LGW_RADIO_TYPE_SX1255: + part_int = pkt_data.freq_hz / (SX125x_32MHz_FRAC << 7); /* integer part, gives the MSB */ + part_frac = ((pkt_data.freq_hz % (SX125x_32MHz_FRAC << 7)) << 9) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */ + break; + case LGW_RADIO_TYPE_SX1257: + part_int = pkt_data.freq_hz / (SX125x_32MHz_FRAC << 8); /* integer part, gives the MSB */ + part_frac = ((pkt_data.freq_hz % (SX125x_32MHz_FRAC << 8)) << 8) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */ + break; + default: + DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d FOR RADIO TYPE\n", rf_radio_type[0]); + break; + } + + buff[0] = 0xFF & part_int; /* Most Significant Byte */ + buff[1] = 0xFF & (part_frac >> 8); /* middle byte */ + buff[2] = 0xFF & part_frac; /* Least Significant Byte */ + + /* metadata 3 to 6, timestamp trigger value */ + /* TX state machine must be triggered at (T0 - lgw_i_tx_start_delay_us) for packet to start being emitted at T0 */ + if (pkt_data.tx_mode == TIMESTAMPED) + { + count_trig = pkt_data.count_us - (uint32_t)tx_start_delay; + buff[3] = 0xFF & (count_trig >> 24); + buff[4] = 0xFF & (count_trig >> 16); + buff[5] = 0xFF & (count_trig >> 8); + buff[6] = 0xFF & count_trig; + } + + /* parameters depending on modulation */ + if (pkt_data.modulation == MOD_LORA) { + /* metadata 7, modulation type, radio chain selection and TX power */ + buff[7] = (0x20 & (pkt_data.rf_chain << 5)) | (0x0F & pow_index); /* bit 4 is 0 -> LoRa modulation */ + + buff[8] = 0; /* metadata 8, not used */ + + /* metadata 9, CRC, LoRa CR & SF */ + switch (pkt_data.datarate) { + case DR_LORA_SF7: buff[9] = 7; break; + case DR_LORA_SF8: buff[9] = 8; break; + case DR_LORA_SF9: buff[9] = 9; break; + case DR_LORA_SF10: buff[9] = 10; break; + case DR_LORA_SF11: buff[9] = 11; break; + case DR_LORA_SF12: buff[9] = 12; break; + default: DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", pkt_data.datarate); + } + switch (pkt_data.coderate) { + case CR_LORA_4_5: buff[9] |= 1 << 4; break; + case CR_LORA_4_6: buff[9] |= 2 << 4; break; + case CR_LORA_4_7: buff[9] |= 3 << 4; break; + case CR_LORA_4_8: buff[9] |= 4 << 4; break; + default: DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", pkt_data.coderate); + } + if (pkt_data.no_crc == false) { + buff[9] |= 0x80; /* set 'CRC enable' bit */ + } else { + DEBUG_MSG("Info: packet will be sent without CRC\n"); + } + + /* metadata 10, payload size */ + buff[10] = pkt_data.size; + + /* metadata 11, implicit header, modulation bandwidth, PPM offset & polarity */ + switch (pkt_data.bandwidth) { + case BW_125KHZ: buff[11] = 0; break; + case BW_250KHZ: buff[11] = 1; break; + case BW_500KHZ: buff[11] = 2; break; + default: DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", pkt_data.bandwidth); + } + if (pkt_data.no_header == true) { + buff[11] |= 0x04; /* set 'implicit header' bit */ + } + if (SET_PPM_ON(pkt_data.bandwidth,pkt_data.datarate)) { + buff[11] |= 0x08; /* set 'PPM offset' bit at 1 */ + } + if (pkt_data.invert_pol == true) { + buff[11] |= 0x10; /* set 'TX polarity' bit at 1 */ + } + + /* metadata 12 & 13, LoRa preamble size */ + if (pkt_data.preamble == 0) { /* if not explicit, use recommended LoRa preamble size */ + pkt_data.preamble = STD_LORA_PREAMBLE; + } else if (pkt_data.preamble < MIN_LORA_PREAMBLE) { /* enforce minimum preamble size */ + pkt_data.preamble = MIN_LORA_PREAMBLE; + DEBUG_MSG("Note: preamble length adjusted to respect minimum LoRa preamble size\n"); + } + buff[12] = 0xFF & (pkt_data.preamble >> 8); + buff[13] = 0xFF & pkt_data.preamble; + + /* metadata 14 & 15, not used */ + buff[14] = 0; + buff[15] = 0; + + /* MSB of RF frequency is now used in AGC firmware to implement large/narrow filtering in SX1257/55 */ + buff[0] &= 0x3F; /* Unset 2 MSBs of frequency code */ + if (pkt_data.bandwidth == BW_500KHZ) { + buff[0] |= 0x80; /* Set MSB bit to enlarge analog filter for 500kHz BW */ + } + + /* Set MSB-1 bit to enable digital filter if required */ + if (tx_notch_enable == true) { + DEBUG_MSG("INFO: Enabling TX notch filter\n"); + buff[0] |= 0x40; + } + } else if (pkt_data.modulation == MOD_FSK) { + /* metadata 7, modulation type, radio chain selection and TX power */ + buff[7] = (0x20 & (pkt_data.rf_chain << 5)) | 0x10 | (0x0F & pow_index); /* bit 4 is 1 -> FSK modulation */ + + buff[8] = 0; /* metadata 8, not used */ + + /* metadata 9, frequency deviation */ + buff[9] = pkt_data.f_dev; + + /* metadata 10, payload size */ + buff[10] = pkt_data.size; + /* TODO: how to handle 255 bytes packets ?!? */ + + /* metadata 11, packet mode, CRC, encoding */ + buff[11] = 0x01 | (pkt_data.no_crc?0:0x02) | (0x02 << 2); /* always in variable length packet mode, whitening, and CCITT CRC if CRC is not disabled */ + + /* metadata 12 & 13, FSK preamble size */ + if (pkt_data.preamble == 0) { /* if not explicit, use LoRa MAC preamble size */ + pkt_data.preamble = STD_FSK_PREAMBLE; + } else if (pkt_data.preamble < MIN_FSK_PREAMBLE) { /* enforce minimum preamble size */ + pkt_data.preamble = MIN_FSK_PREAMBLE; + DEBUG_MSG("Note: preamble length adjusted to respect minimum FSK preamble size\n"); + } + buff[12] = 0xFF & (pkt_data.preamble >> 8); + buff[13] = 0xFF & pkt_data.preamble; + + /* metadata 14 & 15, FSK baudrate */ + fsk_dr_div = (uint16_t)((uint32_t)LGW_XTAL_FREQU / pkt_data.datarate); /* Ok for datarate between 500bps and 250kbps */ + buff[14] = 0xFF & (fsk_dr_div >> 8); + buff[15] = 0xFF & fsk_dr_div; + + /* insert payload size in the packet for variable mode */ + buff[16] = pkt_data.size; + ++transfer_size; /* one more byte to transfer to the TX modem */ + ++payload_offset; /* start the payload with one more byte of offset */ + + /* MSB of RF frequency is now used in AGC firmware to implement large/narrow filtering in SX1257/55 */ + buff[0] &= 0x7F; /* Always use narrow band for FSK (force MSB to 0) */ + + } else { + DEBUG_MSG("ERROR: INVALID TX MODULATION..\n"); + return LGW_HAL_ERROR; + } + + /* Configure TX start delay based on TX notch filter */ + lgw_reg_w(LGW_TX_START_DELAY, tx_start_delay); + + /* copy payload from user struct to buffer containing metadata */ + memcpy((void *)(buff + payload_offset), (void *)(pkt_data.payload), pkt_data.size); + + /* reset TX command flags */ + lgw_abort_tx(); + + /* put metadata + payload in the TX data buffer */ + lgw_reg_w(LGW_TX_DATA_BUF_ADDR, 0); + lgw_reg_wb(LGW_TX_DATA_BUF_DATA, buff, transfer_size); + DEBUG_ARRAY(i, transfer_size, buff); + + x = lbt_is_channel_free(&pkt_data, tx_start_delay, &tx_allowed); + if (x != LGW_LBT_SUCCESS) { + DEBUG_MSG("ERROR: Failed to check channel availability for TX\n"); + return LGW_HAL_ERROR; + } + if (tx_allowed == true) { + switch(pkt_data.tx_mode) { + case IMMEDIATE: + lgw_reg_w(LGW_TX_TRIG_IMMEDIATE, 1); + break; + + case TIMESTAMPED: + lgw_reg_w(LGW_TX_TRIG_DELAYED, 1); + break; + + case ON_GPS: + lgw_reg_w(LGW_TX_TRIG_GPS, 1); + break; + + default: + DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", pkt_data.tx_mode); + return LGW_HAL_ERROR; + } + } else { + DEBUG_MSG("ERROR: Cannot send packet, channel is busy (LBT)\n"); + return LGW_LBT_ISSUE; + } + + return LGW_HAL_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_status(uint8_t select, uint8_t *code) { + int32_t read_value; + + /* check input variables */ + CHECK_NULL(code); + + if (select == TX_STATUS) { + lgw_reg_r(LGW_TX_STATUS, &read_value); + if (lgw_is_started == false) { + *code = TX_OFF; + } else if ((read_value & 0x10) == 0) { /* bit 4 @1: TX programmed */ + *code = TX_FREE; + } else if ((read_value & 0x60) != 0) { /* bit 5 or 6 @1: TX sequence */ + *code = TX_EMITTING; + } else { + *code = TX_SCHEDULED; + } + return LGW_HAL_SUCCESS; + + } else if (select == RX_STATUS) { + *code = RX_STATUS_UNKNOWN; /* todo */ + return LGW_HAL_SUCCESS; + + } else { + DEBUG_MSG("ERROR: SELECTION INVALID, NO STATUS TO RETURN\n"); + return LGW_HAL_ERROR; + } + +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_abort_tx(void) { + int i; + + i = lgw_reg_w(LGW_TX_TRIG_ALL, 0); + + if (i == LGW_REG_SUCCESS) return LGW_HAL_SUCCESS; + else return LGW_HAL_ERROR; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_get_trigcnt(uint32_t* trig_cnt_us) { + int i; + int32_t val; + + i = lgw_reg_r(LGW_TIMESTAMP, &val); + if (i == LGW_REG_SUCCESS) { + *trig_cnt_us = (uint32_t)val; + return LGW_HAL_SUCCESS; + } else { + return LGW_HAL_ERROR; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +const char* lgw_version_info() { + return lgw_version_string; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +uint32_t lgw_time_on_air(struct lgw_pkt_tx_s *packet) { + int32_t val; + uint8_t SF, H, DE; + uint16_t BW; + uint32_t payloadSymbNb, Tpacket; + double Tsym, Tpreamble, Tpayload, Tfsk; + + if (packet == NULL) { + DEBUG_MSG("ERROR: Failed to compute time on air, wrong parameter\n"); + return 0; + } + + if (packet->modulation == MOD_LORA) { + /* Get bandwidth */ + val = lgw_bw_getval(packet->bandwidth); + if (val != -1) { + BW = (uint16_t)(val / 1E3); + } else { + DEBUG_PRINTF("ERROR: Cannot compute time on air for this packet, unsupported bandwidth (0x%02X)\n", packet->bandwidth); + return 0; + } + + /* Get datarate */ + val = lgw_sf_getval(packet->datarate); + if (val != -1) { + SF = (uint8_t)val; + } else { + DEBUG_PRINTF("ERROR: Cannot compute time on air for this packet, unsupported datarate (0x%02X)\n", packet->datarate); + return 0; + } + + /* Duration of 1 symbol */ + Tsym = pow(2, SF) / BW; + + /* Duration of preamble */ + Tpreamble = ((double)(packet->preamble) + 4.25) * Tsym; + + /* Duration of payload */ + H = (packet->no_header==false) ? 0 : 1; /* header is always enabled, except for beacons */ + DE = (SF >= 11) ? 1 : 0; /* Low datarate optimization enabled for SF11 and SF12 */ + + payloadSymbNb = 8 + (ceil((double)(8*packet->size - 4*SF + 28 + 16 - 20*H) / (double)(4*(SF - 2*DE))) * (packet->coderate + 4)); /* Explicitely cast to double to keep precision of the division */ + + Tpayload = payloadSymbNb * Tsym; + + /* Duration of packet */ + Tpacket = Tpreamble + Tpayload; + } else if (packet->modulation == MOD_FSK) { + /* PREAMBLE + SYNC_WORD + PKT_LEN + PKT_PAYLOAD + CRC + PREAMBLE: default 5 bytes + SYNC_WORD: default 3 bytes + PKT_LEN: 1 byte (variable length mode) + PKT_PAYLOAD: x bytes + CRC: 0 or 2 bytes + */ + Tfsk = (8 * (double)(packet->preamble + fsk_sync_word_size + 1 + packet->size + ((packet->no_crc == true) ? 0 : 2)) / (double)packet->datarate) * 1E3; + + /* Duration of packet */ + Tpacket = (uint32_t)Tfsk + 1; /* add margin for rounding */ + } else { + Tpacket = 0; + DEBUG_PRINTF("ERROR: Cannot compute time on air for this packet, unsupported modulation (0x%02X)\n", packet->modulation); + } + + return Tpacket; +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/src/loragw_lbt.c b/libloragw/src/loragw_lbt.c new file mode 100644 index 0000000..9c43521 --- /dev/null +++ b/libloragw/src/loragw_lbt.c @@ -0,0 +1,391 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Functions used to handle the Listen Before Talk feature + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Michael Coracin +*/ + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ +#include <stdio.h> /* printf fprintf */ +#include <stdlib.h> /* abs, labs, llabs */ +#include <string.h> /* memset */ + +#include "loragw_radio.h" +#include "loragw_aux.h" +#include "loragw_lbt.h" +#include "loragw_fpga.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#if DEBUG_LBT == 1 + #define DEBUG_MSG(str) fprintf(stderr, str) + #define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args) + #define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_REG_ERROR;} +#else + #define DEBUG_MSG(str) + #define DEBUG_PRINTF(fmt, args...) + #define CHECK_NULL(a) if(a==NULL){return LGW_REG_ERROR;} +#endif + +#define LBT_TIMESTAMP_MASK 0x007FF000 /* 11-bits timestamp */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE TYPES -------------------------------------------------------- */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +/* -------------------------------------------------------------------------- */ +/* --- INTERNAL SHARED VARIABLES -------------------------------------------- */ + +extern void *lgw_spi_target; /*! generic pointer to the SPI device */ +extern uint8_t lgw_spi_mux_mode; /*! current SPI mux mode used */ +extern uint16_t lgw_i_tx_start_delay_us; + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE VARIABLES ---------------------------------------------------- */ + +static bool lbt_enable; +static uint8_t lbt_nb_active_channel; +static int8_t lbt_rssi_target_dBm; +static int8_t lbt_rssi_offset_dB; +static uint32_t lbt_start_freq; +static struct lgw_conf_lbt_chan_s lbt_channel_cfg[LBT_CHANNEL_FREQ_NB]; + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS ---------------------------------------------------- */ + +bool is_equal_freq(uint32_t a, uint32_t b); + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ + +int lbt_setconf(struct lgw_conf_lbt_s * conf) { + int i; + + /* Check input parameters */ + if (conf == NULL) { + return LGW_LBT_ERROR; + } + if ((conf->nb_channel < 1) || (conf->nb_channel > LBT_CHANNEL_FREQ_NB)) { + DEBUG_PRINTF("ERROR: Number of defined LBT channels is out of range (%u)\n", conf->nb_channel); + return LGW_LBT_ERROR; + } + + /* Initialize LBT channels configuration */ + memset(lbt_channel_cfg, 0, sizeof lbt_channel_cfg); + + /* Set internal LBT config according to parameters */ + lbt_enable = conf->enable; + lbt_nb_active_channel = conf->nb_channel; + lbt_rssi_target_dBm = conf->rssi_target; + lbt_rssi_offset_dB = conf->rssi_offset; + + for (i=0; i<lbt_nb_active_channel; i++) { + lbt_channel_cfg[i].freq_hz = conf->channels[i].freq_hz; + lbt_channel_cfg[i].scan_time_us = conf->channels[i].scan_time_us; + } + + return LGW_LBT_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lbt_setup(void) { + int x, i; + int32_t val; + uint32_t freq_offset; + + /* Check if LBT feature is supported by FPGA */ + x = lgw_fpga_reg_r(LGW_FPGA_FEATURE, &val); + if (x != LGW_REG_SUCCESS) { + DEBUG_MSG("ERROR: Failed to read FPGA Features register\n"); + return LGW_LBT_ERROR; + } + if (TAKE_N_BITS_FROM((uint8_t)val, 2, 1) != 1) { + DEBUG_MSG("ERROR: No support for LBT in FPGA\n"); + return LGW_LBT_ERROR; + } + + /* Get FPGA lowest frequency for LBT channels */ + x = lgw_fpga_reg_r(LGW_FPGA_LBT_INITIAL_FREQ, &val); + if (x != LGW_REG_SUCCESS) { + DEBUG_MSG("ERROR: Failed to read LBT initial frequency from FPGA\n"); + return LGW_LBT_ERROR; + } + switch(val) { + case 0: + lbt_start_freq = 915000000; + break; + case 1: + lbt_start_freq = 863000000; + break; + default: + DEBUG_PRINTF("ERROR: LBT start frequency %d is not supported\n", val); + return LGW_LBT_ERROR; + } + + /* Configure SX127x for FSK */ + x = lgw_setup_sx127x(lbt_start_freq, MOD_FSK, LGW_SX127X_RXBW_100K_HZ, lbt_rssi_offset_dB); /* 200KHz LBT channels */ + if (x != LGW_REG_SUCCESS) { + DEBUG_MSG("ERROR: Failed to configure SX127x for LBT\n"); + return LGW_LBT_ERROR; + } + + /* Configure FPGA for LBT */ + val = -2*lbt_rssi_target_dBm; /* Convert RSSI target in dBm to FPGA register format */ + x = lgw_fpga_reg_w(LGW_FPGA_RSSI_TARGET, val); + if (x != LGW_REG_SUCCESS) { + DEBUG_MSG("ERROR: Failed to configure FPGA for LBT\n"); + return LGW_LBT_ERROR; + } + /* Set default values for non-active LBT channels */ + for (i=lbt_nb_active_channel; i<LBT_CHANNEL_FREQ_NB; i++) { + lbt_channel_cfg[i].freq_hz = lbt_start_freq; + lbt_channel_cfg[i].scan_time_us = 128; /* fastest scan for non-active channels */ + } + /* Configure FPGA for both active and non-active LBT channels */ + for (i=0; i<LBT_CHANNEL_FREQ_NB; i++) { + /* Check input parameters */ + if (lbt_channel_cfg[i].freq_hz < lbt_start_freq) { + DEBUG_PRINTF("ERROR: LBT channel frequency is out of range (%u)\n", lbt_channel_cfg[i].freq_hz); + return LGW_LBT_ERROR; + } + if ((lbt_channel_cfg[i].scan_time_us != 128) && (lbt_channel_cfg[i].scan_time_us != 5000)) { + DEBUG_PRINTF("ERROR: LBT channel scan time is not supported (%u)\n", lbt_channel_cfg[i].scan_time_us); + return LGW_LBT_ERROR; + } + /* Configure */ + freq_offset = (lbt_channel_cfg[i].freq_hz - lbt_start_freq) / 100E3; /* 100kHz unit */ + x = lgw_fpga_reg_w(LGW_FPGA_LBT_CH0_FREQ_OFFSET+i, (int32_t)freq_offset); + if (x != LGW_REG_SUCCESS) { + DEBUG_PRINTF("ERROR: Failed to configure FPGA for LBT channel %d (freq offset)\n", i); + return LGW_LBT_ERROR; + } + if (lbt_channel_cfg[i].scan_time_us == 5000) { /* configured to 128 by default */ + x = lgw_fpga_reg_w(LGW_FPGA_LBT_SCAN_TIME_CH0+i, 1); + if (x != LGW_REG_SUCCESS) { + DEBUG_PRINTF("ERROR: Failed to configure FPGA for LBT channel %d (freq offset)\n", i); + return LGW_LBT_ERROR; + } + } + } + + DEBUG_MSG("Note: LBT configuration:\n"); + DEBUG_PRINTF("\tlbt_enable: %d\n", lbt_enable ); + DEBUG_PRINTF("\tlbt_nb_active_channel: %d\n", lbt_nb_active_channel ); + DEBUG_PRINTF("\tlbt_start_freq: %d\n", lbt_start_freq); + DEBUG_PRINTF("\tlbt_rssi_target: %d\n", lbt_rssi_target_dBm ); + for (i=0; i<LBT_CHANNEL_FREQ_NB; i++) { + DEBUG_PRINTF("\tlbt_channel_cfg[%d].freq_hz: %u\n", i, lbt_channel_cfg[i].freq_hz ); + DEBUG_PRINTF("\tlbt_channel_cfg[%d].scan_time_us: %u\n", i, lbt_channel_cfg[i].scan_time_us ); + } + + return LGW_LBT_SUCCESS; + +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lbt_start(void) { + int x; + + x = lgw_fpga_reg_w(LGW_FPGA_CTRL_FEATURE_START, 1); + if (x != LGW_REG_SUCCESS) { + DEBUG_MSG("ERROR: Failed to start LBT FSM\n"); + return LGW_LBT_ERROR; + } + + return LGW_LBT_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lbt_is_channel_free(struct lgw_pkt_tx_s * pkt_data, uint16_t tx_start_delay, bool * tx_allowed) { + int i; + int32_t val; + uint32_t tx_start_time = 0; + uint32_t tx_end_time = 0; + uint32_t delta_time = 0; + uint32_t sx1301_time = 0; + uint32_t lbt_time = 0; + uint32_t lbt_time1 = 0; + uint32_t lbt_time2 = 0; + uint32_t tx_max_time = 0; + int lbt_channel_decod_1 = -1; + int lbt_channel_decod_2 = -1; + uint32_t packet_duration = 0; + + /* Check input parameters */ + if ((pkt_data == NULL) || (tx_allowed == NULL)) { + return LGW_LBT_ERROR; + } + + /* Check if TX is allowed */ + if (lbt_enable == true) { + /* TX allowed for LoRa only */ + if (pkt_data->modulation != MOD_LORA) { + *tx_allowed = false; + DEBUG_PRINTF("INFO: TX is not allowed for this modulation (%x)\n", pkt_data->modulation); + return LGW_LBT_SUCCESS; + } + + /* Get SX1301 time at last PPS */ + lgw_get_trigcnt(&sx1301_time); + + DEBUG_MSG("################################\n"); + switch(pkt_data->tx_mode) { + case TIMESTAMPED: + DEBUG_MSG("tx_mode = TIMESTAMPED\n"); + tx_start_time = pkt_data->count_us & LBT_TIMESTAMP_MASK; + break; + case ON_GPS: + DEBUG_MSG("tx_mode = ON_GPS\n"); + tx_start_time = (sx1301_time + (uint32_t)tx_start_delay + 1000000) & LBT_TIMESTAMP_MASK; + break; + case IMMEDIATE: + DEBUG_MSG("ERROR: tx_mode IMMEDIATE is not supported when LBT is enabled\n"); + /* FALLTHROUGH */ + default: + return LGW_LBT_ERROR; + } + + /* Select LBT Channel corresponding to required TX frequency */ + lbt_channel_decod_1 = -1; + lbt_channel_decod_2 = -1; + if (pkt_data->bandwidth == BW_125KHZ) { + for (i=0; i<lbt_nb_active_channel; i++) { + if (is_equal_freq(pkt_data->freq_hz, lbt_channel_cfg[i].freq_hz) == true) { + DEBUG_PRINTF("LBT: select channel %d (%u Hz)\n", i, lbt_channel_cfg[i].freq_hz); + lbt_channel_decod_1 = i; + lbt_channel_decod_2 = i; + if (lbt_channel_cfg[i].scan_time_us == 5000) { + tx_max_time = 4000000; /* 4 seconds */ + } else { /* scan_time_us = 128 */ + tx_max_time = 400000; /* 400 milliseconds */ + } + break; + } + } + } else if (pkt_data->bandwidth == BW_250KHZ) { + /* In case of 250KHz, the TX freq has to be in between 2 consecutive channels of 200KHz BW. + The TX can only be over 2 channels, not more */ + for (i=0; i<(lbt_nb_active_channel-1); i++) { + if ((is_equal_freq(pkt_data->freq_hz, (lbt_channel_cfg[i].freq_hz+lbt_channel_cfg[i+1].freq_hz)/2) == true) && ((lbt_channel_cfg[i+1].freq_hz-lbt_channel_cfg[i].freq_hz)==200E3)) { + DEBUG_PRINTF("LBT: select channels %d,%d (%u Hz)\n", i, i+1, (lbt_channel_cfg[i].freq_hz+lbt_channel_cfg[i+1].freq_hz)/2); + lbt_channel_decod_1 = i; + lbt_channel_decod_2 = i+1; + if (lbt_channel_cfg[i].scan_time_us == 5000) { + tx_max_time = 4000000; /* 4 seconds */ + } else { /* scan_time_us = 128 */ + tx_max_time = 200000; /* 200 milliseconds */ + } + break; + } + } + } else { + /* Nothing to do for now */ + } + + /* Get last time when selected channel was free */ + if ((lbt_channel_decod_1 >= 0) && (lbt_channel_decod_2 >= 0)) { + lgw_fpga_reg_w(LGW_FPGA_LBT_TIMESTAMP_SELECT_CH, (int32_t)lbt_channel_decod_1); + lgw_fpga_reg_r(LGW_FPGA_LBT_TIMESTAMP_CH, &val); + lbt_time = lbt_time1 = (uint32_t)(val & 0x0000FFFF) * 256; /* 16bits (1LSB = 256µs) */ + + if (lbt_channel_decod_1 != lbt_channel_decod_2 ) { + lgw_fpga_reg_w(LGW_FPGA_LBT_TIMESTAMP_SELECT_CH, (int32_t)lbt_channel_decod_2); + lgw_fpga_reg_r(LGW_FPGA_LBT_TIMESTAMP_CH, &val); + lbt_time2 = (uint32_t)(val & 0x0000FFFF) * 256; /* 16bits (1LSB = 256µs) */ + + if (lbt_time2 < lbt_time1) { + lbt_time = lbt_time2; + } + } + } else { + lbt_time = 0; + } + + packet_duration = lgw_time_on_air(pkt_data) * 1000UL; + tx_end_time = (tx_start_time + packet_duration) & LBT_TIMESTAMP_MASK; + if (lbt_time < tx_end_time) { + delta_time = tx_end_time - lbt_time; + } else { + /* It means LBT counter has wrapped */ + printf("LBT: lbt counter has wrapped\n"); + delta_time = (LBT_TIMESTAMP_MASK - lbt_time) + tx_end_time; + } + + DEBUG_PRINTF("sx1301_time = %u\n", sx1301_time & LBT_TIMESTAMP_MASK); + DEBUG_PRINTF("tx_freq = %u\n", pkt_data->freq_hz); + DEBUG_MSG("------------------------------------------------\n"); + DEBUG_PRINTF("packet_duration = %u\n", packet_duration); + DEBUG_PRINTF("tx_start_time = %u\n", tx_start_time); + DEBUG_PRINTF("lbt_time1 = %u\n", lbt_time1); + DEBUG_PRINTF("lbt_time2 = %u\n", lbt_time2); + DEBUG_PRINTF("lbt_time = %u\n", lbt_time); + DEBUG_PRINTF("delta_time = %u\n", delta_time); + DEBUG_MSG("------------------------------------------------\n"); + + /* send data if allowed */ + /* lbt_time: last time when channel was free */ + /* tx_max_time: maximum time allowed to send packet since last free time */ + /* 2048: some margin */ + if ((delta_time < (tx_max_time - 2048)) && (lbt_time != 0)) { + *tx_allowed = true; + } else { + DEBUG_MSG("ERROR: TX request rejected (LBT)\n"); + *tx_allowed = false; + } + } else { + /* Always allow if LBT is disabled */ + *tx_allowed = true; + } + + return LGW_LBT_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +bool lbt_is_enabled(void) { + return lbt_enable; +} + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ + +/* As given frequencies have been converted from float to integer, some aliasing +issues can appear, so we can't simply check for equality, but have to take some +margin */ +bool is_equal_freq(uint32_t a, uint32_t b) { + int64_t diff; + int64_t a64 = (int64_t)a; + int64_t b64 = (int64_t)b; + + /* Calculate the difference */ + diff = llabs(a64 - b64); + + /* Check for acceptable diff range */ + if( diff <= 10000 ) + { + return true; + } + + return false; +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/src/loragw_radio.c b/libloragw/src/loragw_radio.c new file mode 100644 index 0000000..b3046a9 --- /dev/null +++ b/libloragw/src/loragw_radio.c @@ -0,0 +1,562 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Functions used to handle LoRa concentrator radios. + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Michael Coracin +*/ + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ +#include <stdio.h> /* printf fprintf */ + +#include "loragw_sx125x.h" +#include "loragw_sx1272_fsk.h" +#include "loragw_sx1272_lora.h" +#include "loragw_sx1276_fsk.h" +#include "loragw_sx1276_lora.h" +#include "loragw_spi.h" +#include "loragw_aux.h" +#include "loragw_reg.h" +#include "loragw_hal.h" +#include "loragw_radio.h" +#include "loragw_fpga.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#if DEBUG_REG == 1 + #define DEBUG_MSG(str) fprintf(stderr, str) + #define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args) + #define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_REG_ERROR;} +#else + #define DEBUG_MSG(str) + #define DEBUG_PRINTF(fmt, args...) + #define CHECK_NULL(a) if(a==NULL){return LGW_REG_ERROR;} +#endif + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE TYPES -------------------------------------------------------- */ + +/** +@struct lgw_radio_FSK_bandwidth_s +@brief Associate a bandwidth in kHz with its corresponding register values +*/ +struct lgw_sx127x_FSK_bandwidth_s { + uint32_t RxBwKHz; + uint8_t RxBwMant; + uint8_t RxBwExp; +}; + +/** +@struct lgw_radio_type_version_s +@brief Associate a radio type with its corresponding expected version value + read in the radio version register. +*/ +struct lgw_radio_type_version_s { + enum lgw_radio_type_e type; + uint8_t reg_version; +}; + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +#define PLL_LOCK_MAX_ATTEMPTS 5 + +const struct lgw_sx127x_FSK_bandwidth_s sx127x_FskBandwidths[] = +{ + { 2600 , 2, 7 }, /* LGW_SX127X_RXBW_2K6_HZ */ + { 3100 , 1, 7 }, /* LGW_SX127X_RXBW_3K1_HZ */ + { 3900 , 0, 7 }, /* ... */ + { 5200 , 2, 6 }, + { 6300 , 1, 6 }, + { 7800 , 0, 6 }, + { 10400 , 2, 5 }, + { 12500 , 1, 5 }, + { 15600 , 0, 5 }, + { 20800 , 2, 4 }, + { 25000 , 1, 4 }, /* ... */ + { 31300 , 0, 4 }, + { 41700 , 2, 3 }, + { 50000 , 1, 3 }, + { 62500 , 0, 3 }, + { 83333 , 2, 2 }, + { 100000, 1, 2 }, + { 125000, 0, 2 }, + { 166700, 2, 1 }, + { 200000, 1, 1 }, /* ... */ + { 250000, 0, 1 } /* LGW_SX127X_RXBW_250K_HZ */ +}; + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE VARIABLES ---------------------------------------------------- */ + +extern void *lgw_spi_target; /*! generic pointer to the SPI device */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS ---------------------------------------------------- */ + +void sx125x_write(uint8_t channel, uint8_t addr, uint8_t data); +uint8_t sx125x_read(uint8_t channel, uint8_t addr); + +int setup_sx1272_FSK(uint32_t frequency, enum lgw_sx127x_rxbw_e rxbw_khz, int8_t rssi_offset); +int setup_sx1276_FSK(uint32_t frequency, enum lgw_sx127x_rxbw_e rxbw_khz, int8_t rssi_offset); + +int reset_sx127x(enum lgw_radio_type_e radio_type); + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ + +void sx125x_write(uint8_t channel, uint8_t addr, uint8_t data) { + int reg_add, reg_dat, reg_cs; + + /* checking input parameters */ + if (channel >= LGW_RF_CHAIN_NB) { + DEBUG_MSG("ERROR: INVALID RF_CHAIN\n"); + return; + } + if (addr >= 0x7F) { + DEBUG_MSG("ERROR: ADDRESS OUT OF RANGE\n"); + return; + } + + /* selecting the target radio */ + switch (channel) { + case 0: + reg_add = LGW_SPI_RADIO_A__ADDR; + reg_dat = LGW_SPI_RADIO_A__DATA; + reg_cs = LGW_SPI_RADIO_A__CS; + break; + + case 1: + reg_add = LGW_SPI_RADIO_B__ADDR; + reg_dat = LGW_SPI_RADIO_B__DATA; + reg_cs = LGW_SPI_RADIO_B__CS; + break; + + default: + DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", channel); + return; + } + + /* SPI master data write procedure */ + lgw_reg_w(reg_cs, 0); + lgw_reg_w(reg_add, 0x80 | addr); /* MSB at 1 for write operation */ + lgw_reg_w(reg_dat, data); + lgw_reg_w(reg_cs, 1); + lgw_reg_w(reg_cs, 0); + + return; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +uint8_t sx125x_read(uint8_t channel, uint8_t addr) { + int reg_add, reg_dat, reg_cs, reg_rb; + int32_t read_value; + + /* checking input parameters */ + if (channel >= LGW_RF_CHAIN_NB) { + DEBUG_MSG("ERROR: INVALID RF_CHAIN\n"); + return 0; + } + if (addr >= 0x7F) { + DEBUG_MSG("ERROR: ADDRESS OUT OF RANGE\n"); + return 0; + } + + /* selecting the target radio */ + switch (channel) { + case 0: + reg_add = LGW_SPI_RADIO_A__ADDR; + reg_dat = LGW_SPI_RADIO_A__DATA; + reg_cs = LGW_SPI_RADIO_A__CS; + reg_rb = LGW_SPI_RADIO_A__DATA_READBACK; + break; + + case 1: + reg_add = LGW_SPI_RADIO_B__ADDR; + reg_dat = LGW_SPI_RADIO_B__DATA; + reg_cs = LGW_SPI_RADIO_B__CS; + reg_rb = LGW_SPI_RADIO_B__DATA_READBACK; + break; + + default: + DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", channel); + return 0; + } + + /* SPI master data read procedure */ + lgw_reg_w(reg_cs, 0); + lgw_reg_w(reg_add, addr); /* MSB at 0 for read operation */ + lgw_reg_w(reg_dat, 0); + lgw_reg_w(reg_cs, 1); + lgw_reg_w(reg_cs, 0); + lgw_reg_r(reg_rb, &read_value); + + return (uint8_t)read_value; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int setup_sx1272_FSK(uint32_t frequency, enum lgw_sx127x_rxbw_e rxbw_khz, int8_t rssi_offset) { + uint64_t freq_reg; + uint8_t ModulationShaping = 0; + uint8_t PllHop = 1; + uint8_t LnaGain = 1; + uint8_t LnaBoost = 3; + uint8_t AdcBwAuto = 0; + uint8_t AdcBw = 7; + uint8_t AdcLowPwr = 0; + uint8_t AdcTrim = 6; + uint8_t AdcTest = 0; + uint8_t RxBwExp = sx127x_FskBandwidths[rxbw_khz].RxBwExp; + uint8_t RxBwMant = sx127x_FskBandwidths[rxbw_khz].RxBwMant; + uint8_t RssiSmoothing = 5; + uint8_t RssiOffsetReg; + uint8_t reg_val; + int x; + + /* Set in FSK mode */ + x = lgw_sx127x_reg_w(SX1272_REG_OPMODE, 0); + wait_ms(100); + x |= lgw_sx127x_reg_w(SX1272_REG_OPMODE, 0 | (ModulationShaping << 3)); /* Sleep mode, no FSK shaping */ + wait_ms(100); + x |= lgw_sx127x_reg_w(SX1272_REG_OPMODE, 1 | (ModulationShaping << 3)); /* Standby mode, no FSK shaping */ + wait_ms(100); + + /* Set RF carrier frequency */ + x |= lgw_sx127x_reg_w(SX1272_REG_PLLHOP, PllHop << 7); + freq_reg = ((uint64_t)frequency << 19) / (uint64_t)32000000; + x |= lgw_sx127x_reg_w(SX1272_REG_FRFMSB, (freq_reg >> 16) & 0xFF); + x |= lgw_sx127x_reg_w(SX1272_REG_FRFMID, (freq_reg >> 8) & 0xFF); + x |= lgw_sx127x_reg_w(SX1272_REG_FRFLSB, (freq_reg >> 0) & 0xFF); + + /* Config */ + x |= lgw_sx127x_reg_w(SX1272_REG_LNA, LnaBoost | (LnaGain << 5)); /* Improved sensitivity, highest gain */ + x |= lgw_sx127x_reg_w(0x68, AdcBw | (AdcBwAuto << 3)); + x |= lgw_sx127x_reg_w(0x69, AdcTest | (AdcTrim << 4) | (AdcLowPwr << 7)); + + /* set BR and FDEV for 200 kHz bandwidth*/ + x |= lgw_sx127x_reg_w(SX1272_REG_BITRATEMSB, 125); + x |= lgw_sx127x_reg_w(SX1272_REG_BITRATELSB, 0); + x |= lgw_sx127x_reg_w(SX1272_REG_FDEVMSB, 2); + x |= lgw_sx127x_reg_w(SX1272_REG_FDEVLSB, 225); + + /* Config continues... */ + x |= lgw_sx127x_reg_w(SX1272_REG_RXCONFIG, 0); /* Disable AGC */ + RssiOffsetReg = (rssi_offset >= 0) ? (uint8_t)rssi_offset : (uint8_t)(~(-rssi_offset)+1); /* 2's complement */ + x |= lgw_sx127x_reg_w(SX1272_REG_RSSICONFIG, RssiSmoothing | (RssiOffsetReg << 3)); /* Set RSSI smoothing to 64 samples, RSSI offset to given value */ + x |= lgw_sx127x_reg_w(SX1272_REG_RXBW, RxBwExp | (RxBwMant << 3)); + x |= lgw_sx127x_reg_w(SX1272_REG_RXDELAY, 2); + x |= lgw_sx127x_reg_w(SX1272_REG_PLL, 0x10); /* PLL BW set to 75 KHz */ + x |= lgw_sx127x_reg_w(0x47, 1); /* optimize PLL start-up time */ + + if (x != LGW_REG_SUCCESS) { + DEBUG_MSG("ERROR: Failed to configure SX1272\n"); + return x; + } + + /* set Rx continuous mode */ + x = lgw_sx127x_reg_w(SX1272_REG_OPMODE, 5 | (ModulationShaping << 3)); /* Receiver Mode, no FSK shaping */ + wait_ms(500); + x |= lgw_sx127x_reg_r(SX1272_REG_IRQFLAGS1, ®_val); + /* Check if RxReady and ModeReady */ + if ((TAKE_N_BITS_FROM(reg_val, 6, 1) == 0) || (TAKE_N_BITS_FROM(reg_val, 7, 1) == 0) || (x != LGW_REG_SUCCESS)) { + DEBUG_MSG("ERROR: SX1272 failed to enter RX continuous mode\n"); + return LGW_REG_ERROR; + } + wait_ms(500); + + DEBUG_PRINTF("INFO: Successfully configured SX1272 for FSK modulation (rxbw=%d)\n", rxbw_khz); + + return LGW_REG_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int setup_sx1276_FSK(uint32_t frequency, enum lgw_sx127x_rxbw_e rxbw_khz, int8_t rssi_offset) { + uint64_t freq_reg; + uint8_t ModulationShaping = 0; + uint8_t PllHop = 1; + uint8_t LnaGain = 1; + uint8_t LnaBoost = 3; + uint8_t AdcBwAuto = 0; + uint8_t AdcBw = 7; + uint8_t AdcLowPwr = 0; + uint8_t AdcTrim = 6; + uint8_t AdcTest = 0; + uint8_t RxBwExp = sx127x_FskBandwidths[rxbw_khz].RxBwExp; + uint8_t RxBwMant = sx127x_FskBandwidths[rxbw_khz].RxBwMant; + uint8_t RssiSmoothing = 5; + uint8_t RssiOffsetReg; + uint8_t reg_val; + int x; + + /* Set in FSK mode */ + x = lgw_sx127x_reg_w(SX1276_REG_OPMODE, 0); + wait_ms(100); + x |= lgw_sx127x_reg_w(SX1276_REG_OPMODE, 0 | (ModulationShaping << 3)); /* Sleep mode, no FSK shaping */ + wait_ms(100); + x |= lgw_sx127x_reg_w(SX1276_REG_OPMODE, 1 | (ModulationShaping << 3)); /* Standby mode, no FSK shaping */ + wait_ms(100); + + /* Set RF carrier frequency */ + x |= lgw_sx127x_reg_w(SX1276_REG_PLLHOP, PllHop << 7); + freq_reg = ((uint64_t)frequency << 19) / (uint64_t)32000000; + x |= lgw_sx127x_reg_w(SX1276_REG_FRFMSB, (freq_reg >> 16) & 0xFF); + x |= lgw_sx127x_reg_w(SX1276_REG_FRFMID, (freq_reg >> 8) & 0xFF); + x |= lgw_sx127x_reg_w(SX1276_REG_FRFLSB, (freq_reg >> 0) & 0xFF); + + /* Config */ + x |= lgw_sx127x_reg_w(SX1276_REG_LNA, LnaBoost | (LnaGain << 5)); /* Improved sensitivity, highest gain */ + x |= lgw_sx127x_reg_w(0x57, AdcBw | (AdcBwAuto << 3)); + x |= lgw_sx127x_reg_w(0x58, AdcTest | (AdcTrim << 4) | (AdcLowPwr << 7)); + + /* set BR and FDEV for 200 kHz bandwidth*/ + x |= lgw_sx127x_reg_w(SX1276_REG_BITRATEMSB, 125); + x |= lgw_sx127x_reg_w(SX1276_REG_BITRATELSB, 0); + x |= lgw_sx127x_reg_w(SX1276_REG_FDEVMSB, 2); + x |= lgw_sx127x_reg_w(SX1276_REG_FDEVLSB, 225); + + /* Config continues... */ + x |= lgw_sx127x_reg_w(SX1276_REG_RXCONFIG, 0); /* Disable AGC */ + RssiOffsetReg = (rssi_offset >= 0) ? (uint8_t)rssi_offset : (uint8_t)(~(-rssi_offset)+1); /* 2's complement */ + x |= lgw_sx127x_reg_w(SX1276_REG_RSSICONFIG, RssiSmoothing | (RssiOffsetReg << 3)); /* Set RSSI smoothing to 64 samples, RSSI offset 3dB */ + x |= lgw_sx127x_reg_w(SX1276_REG_RXBW, RxBwExp | (RxBwMant << 3)); + x |= lgw_sx127x_reg_w(SX1276_REG_RXDELAY, 2); + x |= lgw_sx127x_reg_w(SX1276_REG_PLL, 0x10); /* PLL BW set to 75 KHz */ + x |= lgw_sx127x_reg_w(0x43, 1); /* optimize PLL start-up time */ + + if (x != LGW_REG_SUCCESS) { + DEBUG_MSG("ERROR: Failed to configure SX1276\n"); + return x; + } + + /* set Rx continuous mode */ + x = lgw_sx127x_reg_w(SX1276_REG_OPMODE, 5 | (ModulationShaping << 3)); /* Receiver Mode, no FSK shaping */ + wait_ms(500); + x |= lgw_sx127x_reg_r(SX1276_REG_IRQFLAGS1, ®_val); + /* Check if RxReady and ModeReady */ + if ((TAKE_N_BITS_FROM(reg_val, 6, 1) == 0) || (TAKE_N_BITS_FROM(reg_val, 7, 1) == 0) || (x != LGW_REG_SUCCESS)) { + DEBUG_MSG("ERROR: SX1276 failed to enter RX continuous mode\n"); + return LGW_REG_ERROR; + } + wait_ms(500); + + DEBUG_PRINTF("INFO: Successfully configured SX1276 for FSK modulation (rxbw=%d)\n", rxbw_khz); + + return LGW_REG_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int reset_sx127x(enum lgw_radio_type_e radio_type) { + int x; + + switch(radio_type) { + case LGW_RADIO_TYPE_SX1276: + x = lgw_fpga_reg_w(LGW_FPGA_CTRL_RADIO_RESET, 0); + x |= lgw_fpga_reg_w(LGW_FPGA_CTRL_RADIO_RESET, 1); + if (x != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR: Failed to reset sx127x\n"); + return x; + } + break; + case LGW_RADIO_TYPE_SX1272: + x = lgw_fpga_reg_w(LGW_FPGA_CTRL_RADIO_RESET, 1); + x |= lgw_fpga_reg_w(LGW_FPGA_CTRL_RADIO_RESET, 0); + if (x != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR: Failed to reset sx127x\n"); + return x; + } + break; + default: + DEBUG_PRINTF("ERROR: Failed to reset sx127x, not supported (%d)\n", radio_type); + return LGW_REG_ERROR; + } + + return LGW_REG_SUCCESS; +} + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ + +int lgw_setup_sx125x(uint8_t rf_chain, uint8_t rf_clkout, bool rf_enable, uint8_t rf_radio_type, uint32_t freq_hz) { + uint32_t part_int = 0; + uint32_t part_frac = 0; + int cpt_attempts = 0; + + if (rf_chain >= LGW_RF_CHAIN_NB) { + DEBUG_MSG("ERROR: INVALID RF_CHAIN\n"); + return -1; + } + + /* Get version to identify SX1255/57 silicon revision */ + DEBUG_PRINTF("Note: SX125x #%d version register returned 0x%02x\n", rf_chain, sx125x_read(rf_chain, 0x07)); + + /* General radio setup */ + if (rf_clkout == rf_chain) { + sx125x_write(rf_chain, 0x10, SX125x_TX_DAC_CLK_SEL + 2); + DEBUG_PRINTF("Note: SX125x #%d clock output enabled\n", rf_chain); + } else { + sx125x_write(rf_chain, 0x10, SX125x_TX_DAC_CLK_SEL); + DEBUG_PRINTF("Note: SX125x #%d clock output disabled\n", rf_chain); + } + + switch (rf_radio_type) { + case LGW_RADIO_TYPE_SX1255: + sx125x_write(rf_chain, 0x28, SX125x_XOSC_GM_STARTUP + SX125x_XOSC_DISABLE*16); + break; + case LGW_RADIO_TYPE_SX1257: + sx125x_write(rf_chain, 0x26, SX125x_XOSC_GM_STARTUP + SX125x_XOSC_DISABLE*16); + break; + default: + DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d FOR RADIO TYPE\n", rf_radio_type); + break; + } + + if (rf_enable == true) { + /* Tx gain and trim */ + sx125x_write(rf_chain, 0x08, SX125x_TX_MIX_GAIN + SX125x_TX_DAC_GAIN*16); + sx125x_write(rf_chain, 0x0A, SX125x_TX_ANA_BW + SX125x_TX_PLL_BW*32); + sx125x_write(rf_chain, 0x0B, SX125x_TX_DAC_BW); + + /* Rx gain and trim */ + sx125x_write(rf_chain, 0x0C, SX125x_LNA_ZIN + SX125x_RX_BB_GAIN*2 + SX125x_RX_LNA_GAIN*32); + sx125x_write(rf_chain, 0x0D, SX125x_RX_BB_BW + SX125x_RX_ADC_TRIM*4 + SX125x_RX_ADC_BW*32); + sx125x_write(rf_chain, 0x0E, SX125x_ADC_TEMP + SX125x_RX_PLL_BW*2); + + /* set RX PLL frequency */ + switch (rf_radio_type) { + case LGW_RADIO_TYPE_SX1255: + part_int = freq_hz / (SX125x_32MHz_FRAC << 7); /* integer part, gives the MSB */ + part_frac = ((freq_hz % (SX125x_32MHz_FRAC << 7)) << 9) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */ + break; + case LGW_RADIO_TYPE_SX1257: + part_int = freq_hz / (SX125x_32MHz_FRAC << 8); /* integer part, gives the MSB */ + part_frac = ((freq_hz % (SX125x_32MHz_FRAC << 8)) << 8) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */ + break; + default: + DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d FOR RADIO TYPE\n", rf_radio_type); + break; + } + + sx125x_write(rf_chain, 0x01,0xFF & part_int); /* Most Significant Byte */ + sx125x_write(rf_chain, 0x02,0xFF & (part_frac >> 8)); /* middle byte */ + sx125x_write(rf_chain, 0x03,0xFF & part_frac); /* Least Significant Byte */ + + /* start and PLL lock */ + do { + if (cpt_attempts >= PLL_LOCK_MAX_ATTEMPTS) { + DEBUG_MSG("ERROR: FAIL TO LOCK PLL\n"); + return -1; + } + sx125x_write(rf_chain, 0x00, 1); /* enable Xtal oscillator */ + sx125x_write(rf_chain, 0x00, 3); /* Enable RX (PLL+FE) */ + ++cpt_attempts; + DEBUG_PRINTF("Note: SX125x #%d PLL start (attempt %d)\n", rf_chain, cpt_attempts); + wait_ms(1); + } while((sx125x_read(rf_chain, 0x11) & 0x02) == 0); + } else { + DEBUG_PRINTF("Note: SX125x #%d kept in standby mode\n", rf_chain); + } + + return 0; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_sx127x_reg_w(uint8_t address, uint8_t reg_value) { + return lgw_spi_w(lgw_spi_target, LGW_SPI_MUX_MODE1, LGW_SPI_MUX_TARGET_SX127X, address, reg_value); +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_sx127x_reg_r(uint8_t address, uint8_t *reg_value) { + return lgw_spi_r(lgw_spi_target, LGW_SPI_MUX_MODE1, LGW_SPI_MUX_TARGET_SX127X, address, reg_value); +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int lgw_setup_sx127x(uint32_t frequency, uint8_t modulation, enum lgw_sx127x_rxbw_e rxbw_khz, int8_t rssi_offset) { + int x, i; + uint8_t version; + enum lgw_radio_type_e radio_type = LGW_RADIO_TYPE_NONE; + struct lgw_radio_type_version_s supported_radio_type[2] = { + {LGW_RADIO_TYPE_SX1272, 0x22}, + {LGW_RADIO_TYPE_SX1276, 0x12} + }; + + /* Check parameters */ + if (modulation != MOD_FSK) { + DEBUG_PRINTF("ERROR: modulation not supported for SX127x (%u)\n", modulation); + return LGW_REG_ERROR; + } + if (rxbw_khz > LGW_SX127X_RXBW_250K_HZ) { + DEBUG_PRINTF("ERROR: RX bandwidth not supported for SX127x (%u)\n", rxbw_khz); + return LGW_REG_ERROR; + } + + /* Probing radio type */ + for (i = 0; i < (int)(sizeof supported_radio_type); i++) { + /* Reset the radio */ + x = reset_sx127x(supported_radio_type[i].type); + if (x != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR: Failed to reset sx127x\n"); + return x; + } + /* Read version register */ + x = lgw_sx127x_reg_r(0x42, &version); + if (x != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR: Failed to read sx127x version register\n"); + return x; + } + /* Check if we got the expected version */ + if (version != supported_radio_type[i].reg_version) { + DEBUG_PRINTF("INFO: sx127x version register - read:0x%02x, expected:0x%02x\n", version, supported_radio_type[i].reg_version); + continue; + } else { + DEBUG_PRINTF("INFO: sx127x radio has been found (type:%d, version:0x%02x)\n", supported_radio_type[i].type, version); + radio_type = supported_radio_type[i].type; + break; + } + } + if (radio_type == LGW_RADIO_TYPE_NONE) { + DEBUG_MSG("ERROR: sx127x radio has not been found\n"); + return LGW_REG_ERROR; + } + + /* Setup the radio */ + switch (modulation) { + case MOD_FSK: + if (radio_type == LGW_RADIO_TYPE_SX1272) { + x = setup_sx1272_FSK(frequency, rxbw_khz, rssi_offset); + } else { + x = setup_sx1276_FSK(frequency, rxbw_khz, rssi_offset); + } + break; + default: + /* Should not happen */ + break; + } + if (x != LGW_REG_SUCCESS) { + DEBUG_MSG("ERROR: failed to setup SX127x\n"); + return x; + } + + return LGW_REG_SUCCESS; +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/src/loragw_reg.c b/libloragw/src/loragw_reg.c new file mode 100644 index 0000000..14e23cd --- /dev/null +++ b/libloragw/src/loragw_reg.c @@ -0,0 +1,819 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Functions used to handle a single LoRa concentrator. + Registers are addressed by name. + Multi-bytes registers are handled automatically. + Read-modify-write is handled automatically. + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ +#include <stdio.h> /* printf fprintf */ + +#include "loragw_spi.h" +#include "loragw_reg.h" +#include "loragw_fpga.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#if DEBUG_REG == 1 + #define DEBUG_MSG(str) fprintf(stderr, str) + #define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args) + #define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_REG_ERROR;} +#else + #define DEBUG_MSG(str) + #define DEBUG_PRINTF(fmt, args...) + #define CHECK_NULL(a) if(a==NULL){return LGW_REG_ERROR;} +#endif + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +#define PAGE_ADDR 0x00 +#define PAGE_MASK 0x03 + +const uint8_t FPGA_VERSION[] = { 31, 33 }; /* several versions could be supported */ + +/* +auto generated register mapping for C code : 11-Jul-2013 13:20:40 +this file contains autogenerated C struct used to access the LoRa register from the Primer firmware +this file is autogenerated from registers description +293 registers are defined +*/ +const struct lgw_reg_s loregs[LGW_TOTALREGS] = { + {-1,0,0,0,2,0,0}, /* PAGE_REG */ + {-1,0,7,0,1,0,0}, /* SOFT_RESET */ + {-1,1,0,0,8,1,103}, /* VERSION */ + {-1,2,0,0,16,0,0}, /* RX_DATA_BUF_ADDR */ + {-1,4,0,0,8,0,0}, /* RX_DATA_BUF_DATA */ + {-1,5,0,0,8,0,0}, /* TX_DATA_BUF_ADDR */ + {-1,6,0,0,8,0,0}, /* TX_DATA_BUF_DATA */ + {-1,7,0,0,8,0,0}, /* CAPTURE_RAM_ADDR */ + {-1,8,0,0,8,1,0}, /* CAPTURE_RAM_DATA */ + {-1,9,0,0,8,0,0}, /* MCU_PROM_ADDR */ + {-1,10,0,0,8,0,0}, /* MCU_PROM_DATA */ + {-1,11,0,0,8,0,0}, /* RX_PACKET_DATA_FIFO_NUM_STORED */ + {-1,12,0,0,16,1,0}, /* RX_PACKET_DATA_FIFO_ADDR_POINTER */ + {-1,14,0,0,8,1,0}, /* RX_PACKET_DATA_FIFO_STATUS */ + {-1,15,0,0,8,1,0}, /* RX_PACKET_DATA_FIFO_PAYLOAD_SIZE */ + {-1,16,0,0,1,0,0}, /* MBWSSF_MODEM_ENABLE */ + {-1,16,1,0,1,0,0}, /* CONCENTRATOR_MODEM_ENABLE */ + {-1,16,2,0,1,0,0}, /* FSK_MODEM_ENABLE */ + {-1,16,3,0,1,0,0}, /* GLOBAL_EN */ + {-1,17,0,0,1,0,1}, /* CLK32M_EN */ + {-1,17,1,0,1,0,1}, /* CLKHS_EN */ + {-1,18,0,0,1,0,0}, /* START_BIST0 */ + {-1,18,1,0,1,0,0}, /* START_BIST1 */ + {-1,18,2,0,1,0,0}, /* CLEAR_BIST0 */ + {-1,18,3,0,1,0,0}, /* CLEAR_BIST1 */ + {-1,19,0,0,1,1,0}, /* BIST0_FINISHED */ + {-1,19,1,0,1,1,0}, /* BIST1_FINISHED */ + {-1,20,0,0,1,1,0}, /* MCU_AGC_PROG_RAM_BIST_STATUS */ + {-1,20,1,0,1,1,0}, /* MCU_ARB_PROG_RAM_BIST_STATUS */ + {-1,20,2,0,1,1,0}, /* CAPTURE_RAM_BIST_STATUS */ + {-1,20,3,0,1,1,0}, /* CHAN_FIR_RAM0_BIST_STATUS */ + {-1,20,4,0,1,1,0}, /* CHAN_FIR_RAM1_BIST_STATUS */ + {-1,21,0,0,1,1,0}, /* CORR0_RAM_BIST_STATUS */ + {-1,21,1,0,1,1,0}, /* CORR1_RAM_BIST_STATUS */ + {-1,21,2,0,1,1,0}, /* CORR2_RAM_BIST_STATUS */ + {-1,21,3,0,1,1,0}, /* CORR3_RAM_BIST_STATUS */ + {-1,21,4,0,1,1,0}, /* CORR4_RAM_BIST_STATUS */ + {-1,21,5,0,1,1,0}, /* CORR5_RAM_BIST_STATUS */ + {-1,21,6,0,1,1,0}, /* CORR6_RAM_BIST_STATUS */ + {-1,21,7,0,1,1,0}, /* CORR7_RAM_BIST_STATUS */ + {-1,22,0,0,1,1,0}, /* MODEM0_RAM0_BIST_STATUS */ + {-1,22,1,0,1,1,0}, /* MODEM1_RAM0_BIST_STATUS */ + {-1,22,2,0,1,1,0}, /* MODEM2_RAM0_BIST_STATUS */ + {-1,22,3,0,1,1,0}, /* MODEM3_RAM0_BIST_STATUS */ + {-1,22,4,0,1,1,0}, /* MODEM4_RAM0_BIST_STATUS */ + {-1,22,5,0,1,1,0}, /* MODEM5_RAM0_BIST_STATUS */ + {-1,22,6,0,1,1,0}, /* MODEM6_RAM0_BIST_STATUS */ + {-1,22,7,0,1,1,0}, /* MODEM7_RAM0_BIST_STATUS */ + {-1,23,0,0,1,1,0}, /* MODEM0_RAM1_BIST_STATUS */ + {-1,23,1,0,1,1,0}, /* MODEM1_RAM1_BIST_STATUS */ + {-1,23,2,0,1,1,0}, /* MODEM2_RAM1_BIST_STATUS */ + {-1,23,3,0,1,1,0}, /* MODEM3_RAM1_BIST_STATUS */ + {-1,23,4,0,1,1,0}, /* MODEM4_RAM1_BIST_STATUS */ + {-1,23,5,0,1,1,0}, /* MODEM5_RAM1_BIST_STATUS */ + {-1,23,6,0,1,1,0}, /* MODEM6_RAM1_BIST_STATUS */ + {-1,23,7,0,1,1,0}, /* MODEM7_RAM1_BIST_STATUS */ + {-1,24,0,0,1,1,0}, /* MODEM0_RAM2_BIST_STATUS */ + {-1,24,1,0,1,1,0}, /* MODEM1_RAM2_BIST_STATUS */ + {-1,24,2,0,1,1,0}, /* MODEM2_RAM2_BIST_STATUS */ + {-1,24,3,0,1,1,0}, /* MODEM3_RAM2_BIST_STATUS */ + {-1,24,4,0,1,1,0}, /* MODEM4_RAM2_BIST_STATUS */ + {-1,24,5,0,1,1,0}, /* MODEM5_RAM2_BIST_STATUS */ + {-1,24,6,0,1,1,0}, /* MODEM6_RAM2_BIST_STATUS */ + {-1,24,7,0,1,1,0}, /* MODEM7_RAM2_BIST_STATUS */ + {-1,25,0,0,1,1,0}, /* MODEM_MBWSSF_RAM0_BIST_STATUS */ + {-1,25,1,0,1,1,0}, /* MODEM_MBWSSF_RAM1_BIST_STATUS */ + {-1,25,2,0,1,1,0}, /* MODEM_MBWSSF_RAM2_BIST_STATUS */ + {-1,26,0,0,1,1,0}, /* MCU_AGC_DATA_RAM_BIST0_STATUS */ + {-1,26,1,0,1,1,0}, /* MCU_AGC_DATA_RAM_BIST1_STATUS */ + {-1,26,2,0,1,1,0}, /* MCU_ARB_DATA_RAM_BIST0_STATUS */ + {-1,26,3,0,1,1,0}, /* MCU_ARB_DATA_RAM_BIST1_STATUS */ + {-1,26,4,0,1,1,0}, /* TX_TOP_RAM_BIST0_STATUS */ + {-1,26,5,0,1,1,0}, /* TX_TOP_RAM_BIST1_STATUS */ + {-1,26,6,0,1,1,0}, /* DATA_MNGT_RAM_BIST0_STATUS */ + {-1,26,7,0,1,1,0}, /* DATA_MNGT_RAM_BIST1_STATUS */ + {-1,27,0,0,4,0,0}, /* GPIO_SELECT_INPUT */ + {-1,28,0,0,4,0,0}, /* GPIO_SELECT_OUTPUT */ + {-1,29,0,0,5,0,0}, /* GPIO_MODE */ + {-1,30,0,0,5,1,0}, /* GPIO_PIN_REG_IN */ + {-1,31,0,0,5,0,0}, /* GPIO_PIN_REG_OUT */ + {-1,32,0,0,8,1,0}, /* MCU_AGC_STATUS */ + {-1,125,0,0,8,1,0}, /* MCU_ARB_STATUS */ + {-1,126,0,0,8,1,1}, /* CHIP_ID */ + {-1,127,0,0,1,0,1}, /* EMERGENCY_FORCE_HOST_CTRL */ + {0,33,0,0,1,0,0}, /* RX_INVERT_IQ */ + {0,33,1,0,1,0,1}, /* MODEM_INVERT_IQ */ + {0,33,2,0,1,0,0}, /* MBWSSF_MODEM_INVERT_IQ */ + {0,33,3,0,1,0,0}, /* RX_EDGE_SELECT */ + {0,33,4,0,1,0,0}, /* MISC_RADIO_EN */ + {0,33,5,0,1,0,0}, /* FSK_MODEM_INVERT_IQ */ + {0,34,0,0,4,0,7}, /* FILTER_GAIN */ + {0,35,0,0,8,0,240}, /* RADIO_SELECT */ + {0,36,0,1,13,0,-384}, /* IF_FREQ_0 */ + {0,38,0,1,13,0,-128}, /* IF_FREQ_1 */ + {0,40,0,1,13,0,128}, /* IF_FREQ_2 */ + {0,42,0,1,13,0,384}, /* IF_FREQ_3 */ + {0,44,0,1,13,0,-384}, /* IF_FREQ_4 */ + {0,46,0,1,13,0,-128}, /* IF_FREQ_5 */ + {0,48,0,1,13,0,128}, /* IF_FREQ_6 */ + {0,50,0,1,13,0,384}, /* IF_FREQ_7 */ + {0,52,0,1,13,0,0}, /* IF_FREQ_8 */ + {0,54,0,1,13,0,0}, /* IF_FREQ_9 */ + {0,64,0,0,1,0,0}, /* CHANN_OVERRIDE_AGC_GAIN */ + {0,64,1,0,4,0,7}, /* CHANN_AGC_GAIN */ + {0,65,0,0,7,0,0}, /* CORR0_DETECT_EN */ + {0,66,0,0,7,0,0}, /* CORR1_DETECT_EN */ + {0,67,0,0,7,0,0}, /* CORR2_DETECT_EN */ + {0,68,0,0,7,0,0}, /* CORR3_DETECT_EN */ + {0,69,0,0,7,0,0}, /* CORR4_DETECT_EN */ + {0,70,0,0,7,0,0}, /* CORR5_DETECT_EN */ + {0,71,0,0,7,0,0}, /* CORR6_DETECT_EN */ + {0,72,0,0,7,0,0}, /* CORR7_DETECT_EN */ + {0,73,0,0,1,0,0}, /* CORR_SAME_PEAKS_OPTION_SF6 */ + {0,73,1,0,1,0,1}, /* CORR_SAME_PEAKS_OPTION_SF7 */ + {0,73,2,0,1,0,1}, /* CORR_SAME_PEAKS_OPTION_SF8 */ + {0,73,3,0,1,0,1}, /* CORR_SAME_PEAKS_OPTION_SF9 */ + {0,73,4,0,1,0,1}, /* CORR_SAME_PEAKS_OPTION_SF10 */ + {0,73,5,0,1,0,1}, /* CORR_SAME_PEAKS_OPTION_SF11 */ + {0,73,6,0,1,0,1}, /* CORR_SAME_PEAKS_OPTION_SF12 */ + {0,74,0,0,4,0,4}, /* CORR_SIG_NOISE_RATIO_SF6 */ + {0,74,4,0,4,0,4}, /* CORR_SIG_NOISE_RATIO_SF7 */ + {0,75,0,0,4,0,4}, /* CORR_SIG_NOISE_RATIO_SF8 */ + {0,75,4,0,4,0,4}, /* CORR_SIG_NOISE_RATIO_SF9 */ + {0,76,0,0,4,0,4}, /* CORR_SIG_NOISE_RATIO_SF10 */ + {0,76,4,0,4,0,4}, /* CORR_SIG_NOISE_RATIO_SF11 */ + {0,77,0,0,4,0,4}, /* CORR_SIG_NOISE_RATIO_SF12 */ + {0,78,0,0,4,0,4}, /* CORR_NUM_SAME_PEAK */ + {0,78,4,0,3,0,5}, /* CORR_MAC_GAIN */ + {0,81,0,0,12,0,0}, /* ADJUST_MODEM_START_OFFSET_RDX4 */ + {0,83,0,0,12,0,4092}, /* ADJUST_MODEM_START_OFFSET_SF12_RDX4 */ + {0,85,0,0,8,0,7}, /* DBG_CORR_SELECT_SF */ + {0,86,0,0,8,0,0}, /* DBG_CORR_SELECT_CHANNEL */ + {0,87,0,0,8,1,0}, /* DBG_DETECT_CPT */ + {0,88,0,0,8,1,0}, /* DBG_SYMB_CPT */ + {0,89,0,0,1,0,1}, /* CHIRP_INVERT_RX */ + {0,89,1,0,1,0,1}, /* DC_NOTCH_EN */ + {0,90,0,0,1,0,0}, /* IMPLICIT_CRC_EN */ + {0,90,1,0,3,0,0}, /* IMPLICIT_CODING_RATE */ + {0,91,0,0,8,0,0}, /* IMPLICIT_PAYLOAD_LENGHT */ + {0,92,0,0,8,0,29}, /* FREQ_TO_TIME_INVERT */ + {0,93,0,0,6,0,9}, /* FREQ_TO_TIME_DRIFT */ + {0,94,0,0,2,0,2}, /* PAYLOAD_FINE_TIMING_GAIN */ + {0,94,2,0,2,0,1}, /* PREAMBLE_FINE_TIMING_GAIN */ + {0,94,4,0,2,0,0}, /* TRACKING_INTEGRAL */ + {0,95,0,0,4,0,1}, /* FRAME_SYNCH_PEAK1_POS */ + {0,95,4,0,4,0,2}, /* FRAME_SYNCH_PEAK2_POS */ + {0,96,0,0,16,0,10}, /* PREAMBLE_SYMB1_NB */ + {0,98,0,0,1,0,1}, /* FRAME_SYNCH_GAIN */ + {0,98,1,0,1,0,1}, /* SYNCH_DETECT_TH */ + {0,99,0,0,4,0,8}, /* LLR_SCALE */ + {0,99,4,0,2,0,2}, /* SNR_AVG_CST */ + {0,100,0,0,7,0,0}, /* PPM_OFFSET */ + {0,101,0,0,8,0,255}, /* MAX_PAYLOAD_LEN */ + {0,102,0,0,1,0,1}, /* ONLY_CRC_EN */ + {0,103,0,0,8,0,0}, /* ZERO_PAD */ + {0,104,0,0,4,0,8}, /* DEC_GAIN_OFFSET */ + {0,104,4,0,4,0,7}, /* CHAN_GAIN_OFFSET */ + {0,105,1,0,1,0,1}, /* FORCE_HOST_RADIO_CTRL */ + {0,105,2,0,1,0,1}, /* FORCE_HOST_FE_CTRL */ + {0,105,3,0,1,0,1}, /* FORCE_DEC_FILTER_GAIN */ + {0,106,0,0,1,0,1}, /* MCU_RST_0 */ + {0,106,1,0,1,0,1}, /* MCU_RST_1 */ + {0,106,2,0,1,0,0}, /* MCU_SELECT_MUX_0 */ + {0,106,3,0,1,0,0}, /* MCU_SELECT_MUX_1 */ + {0,106,4,0,1,1,0}, /* MCU_CORRUPTION_DETECTED_0 */ + {0,106,5,0,1,1,0}, /* MCU_CORRUPTION_DETECTED_1 */ + {0,106,6,0,1,0,0}, /* MCU_SELECT_EDGE_0 */ + {0,106,7,0,1,0,0}, /* MCU_SELECT_EDGE_1 */ + {0,107,0,0,8,0,1}, /* CHANN_SELECT_RSSI */ + {0,108,0,0,8,0,32}, /* RSSI_BB_DEFAULT_VALUE */ + {0,109,0,0,8,0,100}, /* RSSI_DEC_DEFAULT_VALUE */ + {0,110,0,0,8,0,100}, /* RSSI_CHANN_DEFAULT_VALUE */ + {0,111,0,0,5,0,7}, /* RSSI_BB_FILTER_ALPHA */ + {0,112,0,0,5,0,5}, /* RSSI_DEC_FILTER_ALPHA */ + {0,113,0,0,5,0,8}, /* RSSI_CHANN_FILTER_ALPHA */ + {0,114,0,0,6,0,0}, /* IQ_MISMATCH_A_AMP_COEFF */ + {0,115,0,0,6,0,0}, /* IQ_MISMATCH_A_PHI_COEFF */ + {0,116,0,0,6,0,0}, /* IQ_MISMATCH_B_AMP_COEFF */ + {0,116,6,0,1,0,0}, /* IQ_MISMATCH_B_SEL_I */ + {0,117,0,0,6,0,0}, /* IQ_MISMATCH_B_PHI_COEFF */ + {1,33,0,0,1,0,0}, /* TX_TRIG_IMMEDIATE */ + {1,33,1,0,1,0,0}, /* TX_TRIG_DELAYED */ + {1,33,2,0,1,0,0}, /* TX_TRIG_GPS */ + {1,34,0,0,16,0,0}, /* TX_START_DELAY */ + {1,36,0,0,4,0,1}, /* TX_FRAME_SYNCH_PEAK1_POS */ + {1,36,4,0,4,0,2}, /* TX_FRAME_SYNCH_PEAK2_POS */ + {1,37,0,0,3,0,0}, /* TX_RAMP_DURATION */ + {1,39,0,1,8,0,0}, /* TX_OFFSET_I */ + {1,40,0,1,8,0,0}, /* TX_OFFSET_Q */ + {1,41,0,0,1,0,0}, /* TX_MODE */ + {1,41,1,0,4,0,0}, /* TX_ZERO_PAD */ + {1,41,5,0,1,0,0}, /* TX_EDGE_SELECT */ + {1,41,6,0,1,0,0}, /* TX_EDGE_SELECT_TOP */ + {1,42,0,0,2,0,0}, /* TX_GAIN */ + {1,42,2,0,3,0,5}, /* TX_CHIRP_LOW_PASS */ + {1,42,5,0,2,0,0}, /* TX_FCC_WIDEBAND */ + {1,42,7,0,1,0,1}, /* TX_SWAP_IQ */ + {1,43,0,0,1,0,0}, /* MBWSSF_IMPLICIT_HEADER */ + {1,43,1,0,1,0,0}, /* MBWSSF_IMPLICIT_CRC_EN */ + {1,43,2,0,3,0,0}, /* MBWSSF_IMPLICIT_CODING_RATE */ + {1,44,0,0,8,0,0}, /* MBWSSF_IMPLICIT_PAYLOAD_LENGHT */ + {1,45,0,0,1,0,1}, /* MBWSSF_AGC_FREEZE_ON_DETECT */ + {1,46,0,0,4,0,1}, /* MBWSSF_FRAME_SYNCH_PEAK1_POS */ + {1,46,4,0,4,0,2}, /* MBWSSF_FRAME_SYNCH_PEAK2_POS */ + {1,47,0,0,16,0,10}, /* MBWSSF_PREAMBLE_SYMB1_NB */ + {1,49,0,0,1,0,1}, /* MBWSSF_FRAME_SYNCH_GAIN */ + {1,49,1,0,1,0,1}, /* MBWSSF_SYNCH_DETECT_TH */ + {1,50,0,0,8,0,10}, /* MBWSSF_DETECT_MIN_SINGLE_PEAK */ + {1,51,0,0,3,0,3}, /* MBWSSF_DETECT_TRIG_SAME_PEAK_NB */ + {1,52,0,0,8,0,29}, /* MBWSSF_FREQ_TO_TIME_INVERT */ + {1,53,0,0,6,0,36}, /* MBWSSF_FREQ_TO_TIME_DRIFT */ + {1,54,0,0,12,0,0}, /* MBWSSF_PPM_CORRECTION */ + {1,56,0,0,2,0,2}, /* MBWSSF_PAYLOAD_FINE_TIMING_GAIN */ + {1,56,2,0,2,0,1}, /* MBWSSF_PREAMBLE_FINE_TIMING_GAIN */ + {1,56,4,0,2,0,0}, /* MBWSSF_TRACKING_INTEGRAL */ + {1,57,0,0,8,0,0}, /* MBWSSF_ZERO_PAD */ + {1,58,0,0,2,0,0}, /* MBWSSF_MODEM_BW */ + {1,58,2,0,1,0,0}, /* MBWSSF_RADIO_SELECT */ + {1,58,3,0,1,0,1}, /* MBWSSF_RX_CHIRP_INVERT */ + {1,59,0,0,4,0,8}, /* MBWSSF_LLR_SCALE */ + {1,59,4,0,2,0,3}, /* MBWSSF_SNR_AVG_CST */ + {1,59,6,0,1,0,0}, /* MBWSSF_PPM_OFFSET */ + {1,60,0,0,4,0,7}, /* MBWSSF_RATE_SF */ + {1,60,4,0,1,0,1}, /* MBWSSF_ONLY_CRC_EN */ + {1,61,0,0,8,0,255}, /* MBWSSF_MAX_PAYLOAD_LEN */ + {1,62,0,0,8,1,128}, /* TX_STATUS */ + {1,63,0,0,3,0,0}, /* FSK_CH_BW_EXPO */ + {1,63,3,0,3,0,0}, /* FSK_RSSI_LENGTH */ + {1,63,6,0,1,0,0}, /* FSK_RX_INVERT */ + {1,63,7,0,1,0,0}, /* FSK_PKT_MODE */ + {1,64,0,0,3,0,0}, /* FSK_PSIZE */ + {1,64,3,0,1,0,0}, /* FSK_CRC_EN */ + {1,64,4,0,2,0,0}, /* FSK_DCFREE_ENC */ + {1,64,6,0,1,0,0}, /* FSK_CRC_IBM */ + {1,65,0,0,5,0,0}, /* FSK_ERROR_OSR_TOL */ + {1,65,7,0,1,0,0}, /* FSK_RADIO_SELECT */ + {1,66,0,0,16,0,0}, /* FSK_BR_RATIO */ + {1,68,0,0,32,0,0}, /* FSK_REF_PATTERN_LSB */ + {1,72,0,0,32,0,0}, /* FSK_REF_PATTERN_MSB */ + {1,76,0,0,8,0,0}, /* FSK_PKT_LENGTH */ + {1,77,0,0,1,0,1}, /* FSK_TX_GAUSSIAN_EN */ + {1,77,1,0,2,0,0}, /* FSK_TX_GAUSSIAN_SELECT_BT */ + {1,77,3,0,1,0,1}, /* FSK_TX_PATTERN_EN */ + {1,77,4,0,1,0,0}, /* FSK_TX_PREAMBLE_SEQ */ + {1,77,5,0,3,0,0}, /* FSK_TX_PSIZE */ + {1,80,0,0,8,0,0}, /* FSK_NODE_ADRS */ + {1,81,0,0,8,0,0}, /* FSK_BROADCAST */ + {1,82,0,0,1,0,1}, /* FSK_AUTO_AFC_ON */ + {1,83,0,0,10,0,0}, /* FSK_PATTERN_TIMEOUT_CFG */ + {2,33,0,0,8,0,0}, /* SPI_RADIO_A__DATA */ + {2,34,0,0,8,1,0}, /* SPI_RADIO_A__DATA_READBACK */ + {2,35,0,0,8,0,0}, /* SPI_RADIO_A__ADDR */ + {2,37,0,0,1,0,0}, /* SPI_RADIO_A__CS */ + {2,38,0,0,8,0,0}, /* SPI_RADIO_B__DATA */ + {2,39,0,0,8,1,0}, /* SPI_RADIO_B__DATA_READBACK */ + {2,40,0,0,8,0,0}, /* SPI_RADIO_B__ADDR */ + {2,42,0,0,1,0,0}, /* SPI_RADIO_B__CS */ + {2,43,0,0,1,0,0}, /* RADIO_A_EN */ + {2,43,1,0,1,0,0}, /* RADIO_B_EN */ + {2,43,2,0,1,0,1}, /* RADIO_RST */ + {2,43,3,0,1,0,0}, /* LNA_A_EN */ + {2,43,4,0,1,0,0}, /* PA_A_EN */ + {2,43,5,0,1,0,0}, /* LNA_B_EN */ + {2,43,6,0,1,0,0}, /* PA_B_EN */ + {2,44,0,0,2,0,0}, /* PA_GAIN */ + {2,45,0,0,4,0,2}, /* LNA_A_CTRL_LUT */ + {2,45,4,0,4,0,4}, /* PA_A_CTRL_LUT */ + {2,46,0,0,4,0,2}, /* LNA_B_CTRL_LUT */ + {2,46,4,0,4,0,4}, /* PA_B_CTRL_LUT */ + {2,47,0,0,5,0,0}, /* CAPTURE_SOURCE */ + {2,47,5,0,1,0,0}, /* CAPTURE_START */ + {2,47,6,0,1,0,0}, /* CAPTURE_FORCE_TRIGGER */ + {2,47,7,0,1,0,0}, /* CAPTURE_WRAP */ + {2,48,0,0,16,0,0}, /* CAPTURE_PERIOD */ + {2,51,0,0,8,1,0}, /* MODEM_STATUS */ + {2,52,0,0,8,1,0}, /* VALID_HEADER_COUNTER_0 */ + {2,54,0,0,8,1,0}, /* VALID_PACKET_COUNTER_0 */ + {2,56,0,0,8,1,0}, /* VALID_HEADER_COUNTER_MBWSSF */ + {2,57,0,0,8,1,0}, /* VALID_HEADER_COUNTER_FSK */ + {2,58,0,0,8,1,0}, /* VALID_PACKET_COUNTER_MBWSSF */ + {2,59,0,0,8,1,0}, /* VALID_PACKET_COUNTER_FSK */ + {2,60,0,0,8,1,0}, /* CHANN_RSSI */ + {2,61,0,0,8,1,0}, /* BB_RSSI */ + {2,62,0,0,8,1,0}, /* DEC_RSSI */ + {2,63,0,0,8,1,0}, /* DBG_MCU_DATA */ + {2,64,0,0,8,1,0}, /* DBG_ARB_MCU_RAM_DATA */ + {2,65,0,0,8,1,0}, /* DBG_AGC_MCU_RAM_DATA */ + {2,66,0,0,16,1,0}, /* NEXT_PACKET_CNT */ + {2,68,0,0,16,1,0}, /* ADDR_CAPTURE_COUNT */ + {2,70,0,0,32,1,0}, /* TIMESTAMP */ + {2,74,0,0,4,1,0}, /* DBG_CHANN0_GAIN */ + {2,74,4,0,4,1,0}, /* DBG_CHANN1_GAIN */ + {2,75,0,0,4,1,0}, /* DBG_CHANN2_GAIN */ + {2,75,4,0,4,1,0}, /* DBG_CHANN3_GAIN */ + {2,76,0,0,4,1,0}, /* DBG_CHANN4_GAIN */ + {2,76,4,0,4,1,0}, /* DBG_CHANN5_GAIN */ + {2,77,0,0,4,1,0}, /* DBG_CHANN6_GAIN */ + {2,77,4,0,4,1,0}, /* DBG_CHANN7_GAIN */ + {2,78,0,0,4,1,0}, /* DBG_DEC_FILT_GAIN */ + {2,79,0,0,3,1,0}, /* SPI_DATA_FIFO_PTR */ + {2,79,3,0,3,1,0}, /* PACKET_DATA_FIFO_PTR */ + {2,80,0,0,8,0,0}, /* DBG_ARB_MCU_RAM_ADDR */ + {2,81,0,0,8,0,0}, /* DBG_AGC_MCU_RAM_ADDR */ + {2,82,0,0,1,0,0}, /* SPI_MASTER_CHIP_SELECT_POLARITY */ + {2,82,1,0,1,0,0}, /* SPI_MASTER_CPOL */ + {2,82,2,0,1,0,0}, /* SPI_MASTER_CPHA */ + {2,83,0,0,1,0,0}, /* SIG_GEN_ANALYSER_MUX_SEL */ + {2,84,0,0,1,0,0}, /* SIG_GEN_EN */ + {2,84,1,0,1,0,0}, /* SIG_ANALYSER_EN */ + {2,84,2,0,2,0,0}, /* SIG_ANALYSER_AVG_LEN */ + {2,84,4,0,3,0,0}, /* SIG_ANALYSER_PRECISION */ + {2,84,7,0,1,1,0}, /* SIG_ANALYSER_VALID_OUT */ + {2,85,0,0,8,0,0}, /* SIG_GEN_FREQ */ + {2,86,0,0,8,0,0}, /* SIG_ANALYSER_FREQ */ + {2,87,0,0,8,1,0}, /* SIG_ANALYSER_I_OUT */ + {2,88,0,0,8,1,0}, /* SIG_ANALYSER_Q_OUT */ + {2,89,0,0,1,0,0}, /* GPS_EN */ + {2,89,1,0,1,0,1}, /* GPS_POL */ + {2,90,0,1,8,0,0}, /* SW_TEST_REG1 */ + {2,91,2,1,6,0,0}, /* SW_TEST_REG2 */ + {2,92,0,1,16,0,0}, /* SW_TEST_REG3 */ + {2,94,0,0,4,1,0}, /* DATA_MNGT_STATUS */ + {2,95,0,0,5,1,0}, /* DATA_MNGT_CPT_FRAME_ALLOCATED */ + {2,96,0,0,5,1,0}, /* DATA_MNGT_CPT_FRAME_FINISHED */ + {2,97,0,0,5,1,0}, /* DATA_MNGT_CPT_FRAME_READEN */ + {1,33,0,0,8,0,0} /* TX_TRIG_ALL (alias) */ +}; + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE VARIABLES ---------------------------------------------------- */ + +static int lgw_regpage = -1; /*! keep the value of the register page selected */ + +/* -------------------------------------------------------------------------- */ +/* --- INTERNAL SHARED VARIABLES -------------------------------------------- */ + +void *lgw_spi_target = NULL; /*! generic pointer to the SPI device */ +uint8_t lgw_spi_mux_mode = 0; /*! current SPI mux mode used */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS ---------------------------------------------------- */ + +int page_switch(uint8_t target) { + lgw_regpage = PAGE_MASK & target; + lgw_spi_w(lgw_spi_target, lgw_spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, PAGE_ADDR, (uint8_t)lgw_regpage); + return LGW_REG_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +bool check_fpga_version(uint8_t version) { + int i; + + for (i = 0; i < (int)(sizeof FPGA_VERSION); i++) { + if (FPGA_VERSION[i] == version ) { + return true; + } + } + + return false; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int reg_w_align32(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, struct lgw_reg_s r, int32_t reg_value) { + int spi_stat = LGW_REG_SUCCESS; + int i, size_byte; + uint8_t buf[4] = "\x00\x00\x00\x00"; + + if ((r.leng == 8) && (r.offs == 0)) { + /* direct write */ + spi_stat += lgw_spi_w(spi_target, spi_mux_mode, spi_mux_target, r.addr, (uint8_t)reg_value); + } else if ((r.offs + r.leng) <= 8) { + /* single-byte read-modify-write, offs:[0-7], leng:[1-7] */ + spi_stat += lgw_spi_r(spi_target, spi_mux_mode, spi_mux_target, r.addr, &buf[0]); + buf[1] = ((1 << r.leng) - 1) << r.offs; /* bit mask */ + buf[2] = ((uint8_t)reg_value) << r.offs; /* new data offsetted */ + buf[3] = (~buf[1] & buf[0]) | (buf[1] & buf[2]); /* mixing old & new data */ + spi_stat += lgw_spi_w(spi_target, spi_mux_mode, spi_mux_target, r.addr, buf[3]); + } else if ((r.offs == 0) && (r.leng > 0) && (r.leng <= 32)) { + /* multi-byte direct write routine */ + size_byte = (r.leng + 7) / 8; /* add a byte if it's not an exact multiple of 8 */ + for (i=0; i<size_byte; ++i) { + /* big endian register file for a file on N bytes + Least significant byte is stored in buf[0], most one in buf[N-1] */ + buf[i] = (uint8_t)(0x000000FF & reg_value); + reg_value = (reg_value >> 8); + } + spi_stat += lgw_spi_wb(spi_target, spi_mux_mode, spi_mux_target, r.addr, buf, size_byte); /* write the register in one burst */ + } else { + /* register spanning multiple memory bytes but with an offset */ + DEBUG_MSG("ERROR: REGISTER SIZE AND OFFSET ARE NOT SUPPORTED\n"); + return LGW_REG_ERROR; + } + + return spi_stat; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int reg_r_align32(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, struct lgw_reg_s r, int32_t *reg_value) { + int spi_stat = LGW_SPI_SUCCESS; + uint8_t bufu[4] = "\x00\x00\x00\x00"; + int8_t *bufs = (int8_t *)bufu; + int i, size_byte; + uint32_t u = 0; + + if ((r.offs + r.leng) <= 8) { + /* read one byte, then shift and mask bits to get reg value with sign extension if needed */ + spi_stat += lgw_spi_r(spi_target, spi_mux_mode, spi_mux_target, r.addr, &bufu[0]); + bufu[1] = bufu[0] << (8 - r.leng - r.offs); /* left-align the data */ + if (r.sign == true) { + bufs[2] = bufs[1] >> (8 - r.leng); /* right align the data with sign extension (ARITHMETIC right shift) */ + *reg_value = (int32_t)bufs[2]; /* signed pointer -> 32b sign extension */ + } else { + bufu[2] = bufu[1] >> (8 - r.leng); /* right align the data, no sign extension */ + *reg_value = (int32_t)bufu[2]; /* unsigned pointer -> no sign extension */ + } + } else if ((r.offs == 0) && (r.leng > 0) && (r.leng <= 32)) { + size_byte = (r.leng + 7) / 8; /* add a byte if it's not an exact multiple of 8 */ + spi_stat += lgw_spi_rb(spi_target, spi_mux_mode, spi_mux_target, r.addr, bufu, size_byte); + u = 0; + for (i=(size_byte-1); i>=0; --i) { + u = (uint32_t)bufu[i] + (u << 8); /* transform a 4-byte array into a 32 bit word */ + } + if (r.sign == true) { + u = u << (32 - r.leng); /* left-align the data */ + *reg_value = (int32_t)u >> (32 - r.leng); /* right-align the data with sign extension (ARITHMETIC right shift) */ + } else { + *reg_value = (int32_t)u; /* unsigned value -> return 'as is' */ + } + } else { + /* register spanning multiple memory bytes but with an offset */ + DEBUG_MSG("ERROR: REGISTER SIZE AND OFFSET ARE NOT SUPPORTED\n"); + return LGW_REG_ERROR; + } + + return spi_stat; +} + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ + +/* Concentrator connect */ +int lgw_connect(bool spi_only, uint32_t tx_notch_freq) { + int spi_stat = LGW_SPI_SUCCESS; + uint8_t u = 0; + int x; + + /* check SPI link status */ + if (lgw_spi_target != NULL) { + DEBUG_MSG("WARNING: concentrator was already connected\n"); + lgw_spi_close(lgw_spi_target); + } + + /* open the SPI link */ + spi_stat = lgw_spi_open(&lgw_spi_target); + if (spi_stat != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR CONNECTING CONCENTRATOR\n"); + return LGW_REG_ERROR; + } + + if (spi_only == false ) { + /* Detect if the gateway has an FPGA with SPI mux header support */ + /* First, we assume there is an FPGA, and try to read its version */ + spi_stat = lgw_spi_r(lgw_spi_target, LGW_SPI_MUX_MODE1, LGW_SPI_MUX_TARGET_FPGA, loregs[LGW_VERSION].addr, &u); + if (spi_stat != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR READING VERSION REGISTER\n"); + return LGW_REG_ERROR; + } + if (check_fpga_version(u) != true) { + /* We failed to read expected FPGA version, so let's assume there is no FPGA */ + DEBUG_PRINTF("INFO: no FPGA detected or version not supported (v%u)\n", u); + lgw_spi_mux_mode = LGW_SPI_MUX_MODE0; + } else { + DEBUG_PRINTF("INFO: detected FPGA with SPI mux header (v%u)\n", u); + lgw_spi_mux_mode = LGW_SPI_MUX_MODE1; + /* FPGA Soft Reset */ + lgw_spi_w(lgw_spi_target, lgw_spi_mux_mode, LGW_SPI_MUX_TARGET_FPGA, 0, 1); + lgw_spi_w(lgw_spi_target, lgw_spi_mux_mode, LGW_SPI_MUX_TARGET_FPGA, 0, 0); + /* FPGA configure */ + x = lgw_fpga_configure(tx_notch_freq); + if (x != LGW_REG_SUCCESS) { + DEBUG_MSG("ERROR CONFIGURING FPGA\n"); + return LGW_REG_ERROR; + } + } + + /* check SX1301 version */ + spi_stat = lgw_spi_r(lgw_spi_target, lgw_spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, loregs[LGW_VERSION].addr, &u); + if (spi_stat != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR READING CHIP VERSION REGISTER\n"); + return LGW_REG_ERROR; + } + if (u != loregs[LGW_VERSION].dflt) { + DEBUG_PRINTF("ERROR: NOT EXPECTED CHIP VERSION (v%u)\n", u); + return LGW_REG_ERROR; + } + + /* write 0 to the page/reset register */ + spi_stat = lgw_spi_w(lgw_spi_target, lgw_spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, loregs[LGW_PAGE_REG].addr, 0); + if (spi_stat != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR WRITING PAGE REGISTER\n"); + return LGW_REG_ERROR; + } else { + lgw_regpage = 0; + } + } + + DEBUG_MSG("Note: success connecting the concentrator\n"); + return LGW_REG_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* Concentrator disconnect */ +int lgw_disconnect(void) { + if (lgw_spi_target != NULL) { + lgw_spi_close(lgw_spi_target); + lgw_spi_target = NULL; + DEBUG_MSG("Note: success disconnecting the concentrator\n"); + return LGW_REG_SUCCESS; + } else { + DEBUG_MSG("WARNING: concentrator was already disconnected\n"); + return LGW_REG_ERROR; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* soft-reset function */ +int lgw_soft_reset(void) { + /* check if SPI is initialised */ + if ((lgw_spi_target == NULL) || (lgw_regpage < 0)) { + DEBUG_MSG("ERROR: CONCENTRATOR UNCONNECTED\n"); + return LGW_REG_ERROR; + } + lgw_spi_w(lgw_spi_target, lgw_spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0, 0x80); /* 1 -> SOFT_RESET bit */ + lgw_regpage = 0; /* reset the paging static variable */ + return LGW_REG_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* register verification */ +int lgw_reg_check(FILE *f) { + struct lgw_reg_s r; + int32_t read_value; + char ok_msg[] = "+++MATCH+++"; + char notok_msg[] = "###MISMATCH###"; + char *ptr; + int i; + + /* check if SPI is initialised */ + if ((lgw_spi_target == NULL) || (lgw_regpage < 0)) { + DEBUG_MSG("ERROR: CONCENTRATOR UNCONNECTED\n"); + fprintf(f, "ERROR: CONCENTRATOR UNCONNECTED\n"); + return LGW_REG_ERROR; + } + + fprintf(f, "Start of register verification\n"); + for (i=0; i<LGW_TOTALREGS; ++i) { + r = loregs[i]; + lgw_reg_r(i, &read_value); + ptr = (read_value == r.dflt) ? ok_msg : notok_msg; + if (r.sign == true) + fprintf(f, "%s reg number %d read: %d (%x) default: %d (%x)\n", ptr, i, read_value, read_value, r.dflt, r.dflt); + else + fprintf(f, "%s reg number %d read: %u (%x) default: %u (%x)\n", ptr, i, read_value, read_value, r.dflt, r.dflt); + } + fprintf(f, "End of register verification\n"); + + return LGW_REG_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* Write to a register addressed by name */ +int lgw_reg_w(uint16_t register_id, int32_t reg_value) { + int spi_stat = LGW_SPI_SUCCESS; + struct lgw_reg_s r; + + /* check input parameters */ + if (register_id >= LGW_TOTALREGS) { + DEBUG_MSG("ERROR: REGISTER NUMBER OUT OF DEFINED RANGE\n"); + return LGW_REG_ERROR; + } + + /* check if SPI is initialised */ + if ((lgw_spi_target == NULL) || (lgw_regpage < 0)) { + DEBUG_MSG("ERROR: CONCENTRATOR UNCONNECTED\n"); + return LGW_REG_ERROR; + } + + /* intercept direct access to PAGE_REG & SOFT_RESET */ + if (register_id == LGW_PAGE_REG) { + page_switch(reg_value); + return LGW_REG_SUCCESS; + } else if (register_id == LGW_SOFT_RESET) { + /* only reset if lsb is 1 */ + if ((reg_value & 0x01) != 0) + lgw_soft_reset(); + return LGW_REG_SUCCESS; + } + + /* get register struct from the struct array */ + r = loregs[register_id]; + + /* reject write to read-only registers */ + if (r.rdon == 1){ + DEBUG_MSG("ERROR: TRYING TO WRITE A READ-ONLY REGISTER\n"); + return LGW_REG_ERROR; + } + + /* select proper register page if needed */ + if ((r.page != -1) && (r.page != lgw_regpage)) { + spi_stat += page_switch(r.page); + } + + spi_stat += reg_w_align32(lgw_spi_target, lgw_spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, r, reg_value); + + if (spi_stat != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR: SPI ERROR DURING REGISTER WRITE\n"); + return LGW_REG_ERROR; + } else { + return LGW_REG_SUCCESS; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* Read to a register addressed by name */ +int lgw_reg_r(uint16_t register_id, int32_t *reg_value) { + int spi_stat = LGW_SPI_SUCCESS; + struct lgw_reg_s r; + + /* check input parameters */ + CHECK_NULL(reg_value); + if (register_id >= LGW_TOTALREGS) { + DEBUG_MSG("ERROR: REGISTER NUMBER OUT OF DEFINED RANGE\n"); + return LGW_REG_ERROR; + } + + /* check if SPI is initialised */ + if ((lgw_spi_target == NULL) || (lgw_regpage < 0)) { + DEBUG_MSG("ERROR: CONCENTRATOR UNCONNECTED\n"); + return LGW_REG_ERROR; + } + + /* get register struct from the struct array */ + r = loregs[register_id]; + + /* select proper register page if needed */ + if ((r.page != -1) && (r.page != lgw_regpage)) { + spi_stat += page_switch(r.page); + } + + spi_stat += reg_r_align32(lgw_spi_target, lgw_spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, r, reg_value); + + if (spi_stat != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR: SPI ERROR DURING REGISTER WRITE\n"); + return LGW_REG_ERROR; + } else { + return LGW_REG_SUCCESS; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* Point to a register by name and do a burst write */ +int lgw_reg_wb(uint16_t register_id, uint8_t *data, uint16_t size) { + int spi_stat = LGW_SPI_SUCCESS; + struct lgw_reg_s r; + + /* check input parameters */ + CHECK_NULL(data); + if (size == 0) { + DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n"); + return LGW_REG_ERROR; + } + if (register_id >= LGW_TOTALREGS) { + DEBUG_MSG("ERROR: REGISTER NUMBER OUT OF DEFINED RANGE\n"); + return LGW_REG_ERROR; + } + + /* check if SPI is initialised */ + if ((lgw_spi_target == NULL) || (lgw_regpage < 0)) { + DEBUG_MSG("ERROR: CONCENTRATOR UNCONNECTED\n"); + return LGW_REG_ERROR; + } + + /* get register struct from the struct array */ + r = loregs[register_id]; + + /* reject write to read-only registers */ + if (r.rdon == 1){ + DEBUG_MSG("ERROR: TRYING TO BURST WRITE A READ-ONLY REGISTER\n"); + return LGW_REG_ERROR; + } + + /* select proper register page if needed */ + if ((r.page != -1) && (r.page != lgw_regpage)) { + spi_stat += page_switch(r.page); + } + + /* do the burst write */ + spi_stat += lgw_spi_wb(lgw_spi_target, lgw_spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, r.addr, data, size); + + if (spi_stat != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR: SPI ERROR DURING REGISTER BURST WRITE\n"); + return LGW_REG_ERROR; + } else { + return LGW_REG_SUCCESS; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* Point to a register by name and do a burst read */ +int lgw_reg_rb(uint16_t register_id, uint8_t *data, uint16_t size) { + int spi_stat = LGW_SPI_SUCCESS; + struct lgw_reg_s r; + + /* check input parameters */ + CHECK_NULL(data); + if (size == 0) { + DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n"); + return LGW_REG_ERROR; + } + if (register_id >= LGW_TOTALREGS) { + DEBUG_MSG("ERROR: REGISTER NUMBER OUT OF DEFINED RANGE\n"); + return LGW_REG_ERROR; + } + + /* check if SPI is initialised */ + if ((lgw_spi_target == NULL) || (lgw_regpage < 0)) { + DEBUG_MSG("ERROR: CONCENTRATOR UNCONNECTED\n"); + return LGW_REG_ERROR; + } + + /* get register struct from the struct array */ + r = loregs[register_id]; + + /* select proper register page if needed */ + if ((r.page != -1) && (r.page != lgw_regpage)) { + spi_stat += page_switch(r.page); + } + + /* do the burst read */ + spi_stat += lgw_spi_rb(lgw_spi_target, lgw_spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, r.addr, data, size); + + if (spi_stat != LGW_SPI_SUCCESS) { + DEBUG_MSG("ERROR: SPI ERROR DURING REGISTER BURST READ\n"); + return LGW_REG_ERROR; + } else { + return LGW_REG_SUCCESS; + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/src/loragw_spi.native.c b/libloragw/src/loragw_spi.native.c new file mode 100644 index 0000000..c01ed1c --- /dev/null +++ b/libloragw/src/loragw_spi.native.c @@ -0,0 +1,385 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Host specific functions to address the LoRa concentrator registers through + a SPI interface. + Single-byte read/write and burst read/write. + Does not handle pagination. + Could be used with multiple SPI ports in parallel (explicit file descriptor) + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include <stdint.h> /* C99 types */ +#include <stdio.h> /* printf fprintf */ +#include <stdlib.h> /* malloc free */ +#include <unistd.h> /* lseek, close */ +#include <fcntl.h> /* open */ +#include <string.h> /* memset */ + +#include <sys/ioctl.h> +#include <linux/spi/spidev.h> + +#include "loragw_spi.h" +#include "loragw_hal.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#if DEBUG_SPI == 1 + #define DEBUG_MSG(str) fprintf(stderr, str) + #define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args) + #define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_SPI_ERROR;} +#else + #define DEBUG_MSG(str) + #define DEBUG_PRINTF(fmt, args...) + #define CHECK_NULL(a) if(a==NULL){return LGW_SPI_ERROR;} +#endif + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +#define READ_ACCESS 0x00 +#define WRITE_ACCESS 0x80 +#define SPI_SPEED 8000000 +#define SPI_DEV_PATH "/dev/spidev0.0" +//#define SPI_DEV_PATH "/dev/spidev32766.0" + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ + +/* SPI initialization and configuration */ +int lgw_spi_open(void **spi_target_ptr) { + int *spi_device = NULL; + int dev; + int a=0, b=0; + int i; + + /* check input variables */ + CHECK_NULL(spi_target_ptr); /* cannot be null, must point on a void pointer (*spi_target_ptr can be null) */ + + /* allocate memory for the device descriptor */ + spi_device = malloc(sizeof(int)); + if (spi_device == NULL) { + DEBUG_MSG("ERROR: MALLOC FAIL\n"); + return LGW_SPI_ERROR; + } + + /* open SPI device */ + dev = open(SPI_DEV_PATH, O_RDWR); + if (dev < 0) { + DEBUG_PRINTF("ERROR: failed to open SPI device %s\n", SPI_DEV_PATH); + return LGW_SPI_ERROR; + } + + /* setting SPI mode to 'mode 0' */ + i = SPI_MODE_0; + a = ioctl(dev, SPI_IOC_WR_MODE, &i); + b = ioctl(dev, SPI_IOC_RD_MODE, &i); + if ((a < 0) || (b < 0)) { + DEBUG_MSG("ERROR: SPI PORT FAIL TO SET IN MODE 0\n"); + close(dev); + free(spi_device); + return LGW_SPI_ERROR; + } + + /* setting SPI max clk (in Hz) */ + i = SPI_SPEED; + a = ioctl(dev, SPI_IOC_WR_MAX_SPEED_HZ, &i); + b = ioctl(dev, SPI_IOC_RD_MAX_SPEED_HZ, &i); + if ((a < 0) || (b < 0)) { + DEBUG_MSG("ERROR: SPI PORT FAIL TO SET MAX SPEED\n"); + close(dev); + free(spi_device); + return LGW_SPI_ERROR; + } + + /* setting SPI to MSB first */ + i = 0; + a = ioctl(dev, SPI_IOC_WR_LSB_FIRST, &i); + b = ioctl(dev, SPI_IOC_RD_LSB_FIRST, &i); + if ((a < 0) || (b < 0)) { + DEBUG_MSG("ERROR: SPI PORT FAIL TO SET MSB FIRST\n"); + close(dev); + free(spi_device); + return LGW_SPI_ERROR; + } + + /* setting SPI to 8 bits per word */ + i = 0; + a = ioctl(dev, SPI_IOC_WR_BITS_PER_WORD, &i); + b = ioctl(dev, SPI_IOC_RD_BITS_PER_WORD, &i); + if ((a < 0) || (b < 0)) { + DEBUG_MSG("ERROR: SPI PORT FAIL TO SET 8 BITS-PER-WORD\n"); + close(dev); + return LGW_SPI_ERROR; + } + + *spi_device = dev; + *spi_target_ptr = (void *)spi_device; + DEBUG_MSG("Note: SPI port opened and configured ok\n"); + return LGW_SPI_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* SPI release */ +int lgw_spi_close(void *spi_target) { + int spi_device; + int a; + + /* check input variables */ + CHECK_NULL(spi_target); + + /* close file & deallocate file descriptor */ + spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */ + a = close(spi_device); + free(spi_target); + + /* determine return code */ + if (a < 0) { + DEBUG_MSG("ERROR: SPI PORT FAILED TO CLOSE\n"); + return LGW_SPI_ERROR; + } else { + DEBUG_MSG("Note: SPI port closed\n"); + return LGW_SPI_SUCCESS; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* Simple write */ +int lgw_spi_w(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t data) { + int spi_device; + uint8_t out_buf[3]; + uint8_t command_size; + struct spi_ioc_transfer k; + int a; + + /* check input variables */ + CHECK_NULL(spi_target); + if ((address & 0x80) != 0) { + DEBUG_MSG("WARNING: SPI address > 127\n"); + } + + spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */ + + /* prepare frame to be sent */ + if (spi_mux_mode == LGW_SPI_MUX_MODE1) { + out_buf[0] = spi_mux_target; + out_buf[1] = WRITE_ACCESS | (address & 0x7F); + out_buf[2] = data; + command_size = 3; + } else { + out_buf[0] = WRITE_ACCESS | (address & 0x7F); + out_buf[1] = data; + command_size = 2; + } + + /* I/O transaction */ + memset(&k, 0, sizeof(k)); /* clear k */ + k.tx_buf = (unsigned long) out_buf; + k.len = command_size; + k.speed_hz = SPI_SPEED; + k.cs_change = 0; + k.bits_per_word = 8; + a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k); + + /* determine return code */ + if (a != (int)k.len) { + DEBUG_MSG("ERROR: SPI WRITE FAILURE\n"); + return LGW_SPI_ERROR; + } else { + DEBUG_MSG("Note: SPI write success\n"); + return LGW_SPI_SUCCESS; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* Simple read */ +int lgw_spi_r(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data) { + int spi_device; + uint8_t out_buf[3]; + uint8_t command_size; + uint8_t in_buf[ARRAY_SIZE(out_buf)]; + struct spi_ioc_transfer k; + int a; + + /* check input variables */ + CHECK_NULL(spi_target); + if ((address & 0x80) != 0) { + DEBUG_MSG("WARNING: SPI address > 127\n"); + } + CHECK_NULL(data); + + spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */ + + /* prepare frame to be sent */ + if (spi_mux_mode == LGW_SPI_MUX_MODE1) { + out_buf[0] = spi_mux_target; + out_buf[1] = READ_ACCESS | (address & 0x7F); + out_buf[2] = 0x00; + command_size = 3; + } else { + out_buf[0] = READ_ACCESS | (address & 0x7F); + out_buf[1] = 0x00; + command_size = 2; + } + + /* I/O transaction */ + memset(&k, 0, sizeof(k)); /* clear k */ + k.tx_buf = (unsigned long) out_buf; + k.rx_buf = (unsigned long) in_buf; + k.len = command_size; + k.cs_change = 0; + a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k); + + /* determine return code */ + if (a != (int)k.len) { + DEBUG_MSG("ERROR: SPI READ FAILURE\n"); + return LGW_SPI_ERROR; + } else { + DEBUG_MSG("Note: SPI read success\n"); + *data = in_buf[command_size - 1]; + return LGW_SPI_SUCCESS; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* Burst (multiple-byte) write */ +int lgw_spi_wb(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data, uint16_t size) { + int spi_device; + uint8_t command[2]; + uint8_t command_size; + struct spi_ioc_transfer k[2]; + int size_to_do, chunk_size, offset; + int byte_transfered = 0; + int i; + + /* check input parameters */ + CHECK_NULL(spi_target); + if ((address & 0x80) != 0) { + DEBUG_MSG("WARNING: SPI address > 127\n"); + } + CHECK_NULL(data); + if (size == 0) { + DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n"); + return LGW_SPI_ERROR; + } + + spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */ + + /* prepare command byte */ + if (spi_mux_mode == LGW_SPI_MUX_MODE1) { + command[0] = spi_mux_target; + command[1] = WRITE_ACCESS | (address & 0x7F); + command_size = 2; + } else { + command[0] = WRITE_ACCESS | (address & 0x7F); + command_size = 1; + } + size_to_do = size; + + /* I/O transaction */ + memset(&k, 0, sizeof(k)); /* clear k */ + k[0].tx_buf = (unsigned long) &command[0]; + k[0].len = command_size; + k[0].cs_change = 0; + k[1].cs_change = 0; + for (i=0; size_to_do > 0; ++i) { + chunk_size = (size_to_do < LGW_BURST_CHUNK) ? size_to_do : LGW_BURST_CHUNK; + offset = i * LGW_BURST_CHUNK; + k[1].tx_buf = (unsigned long)(data + offset); + k[1].len = chunk_size; + byte_transfered += (ioctl(spi_device, SPI_IOC_MESSAGE(2), &k) - k[0].len ); + DEBUG_PRINTF("BURST WRITE: to trans %d # chunk %d # transferred %d \n", size_to_do, chunk_size, byte_transfered); + size_to_do -= chunk_size; /* subtract the quantity of data already transferred */ + } + + /* determine return code */ + if (byte_transfered != size) { + DEBUG_MSG("ERROR: SPI BURST WRITE FAILURE\n"); + return LGW_SPI_ERROR; + } else { + DEBUG_MSG("Note: SPI burst write success\n"); + return LGW_SPI_SUCCESS; + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* Burst (multiple-byte) read */ +int lgw_spi_rb(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data, uint16_t size) { + int spi_device; + uint8_t command[2]; + uint8_t command_size; + struct spi_ioc_transfer k[2]; + int size_to_do, chunk_size, offset; + int byte_transfered = 0; + int i; + + /* check input parameters */ + CHECK_NULL(spi_target); + if ((address & 0x80) != 0) { + DEBUG_MSG("WARNING: SPI address > 127\n"); + } + CHECK_NULL(data); + if (size == 0) { + DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n"); + return LGW_SPI_ERROR; + } + + spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */ + + /* prepare command byte */ + if (spi_mux_mode == LGW_SPI_MUX_MODE1) { + command[0] = spi_mux_target; + command[1] = READ_ACCESS | (address & 0x7F); + command_size = 2; + } else { + command[0] = READ_ACCESS | (address & 0x7F); + command_size = 1; + } + size_to_do = size; + + /* I/O transaction */ + memset(&k, 0, sizeof(k)); /* clear k */ + k[0].tx_buf = (unsigned long) &command[0]; + k[0].len = command_size; + k[0].cs_change = 0; + k[1].cs_change = 0; + for (i=0; size_to_do > 0; ++i) { + chunk_size = (size_to_do < LGW_BURST_CHUNK) ? size_to_do : LGW_BURST_CHUNK; + offset = i * LGW_BURST_CHUNK; + k[1].rx_buf = (unsigned long)(data + offset); + k[1].len = chunk_size; + byte_transfered += (ioctl(spi_device, SPI_IOC_MESSAGE(2), &k) - k[0].len ); + DEBUG_PRINTF("BURST READ: to trans %d # chunk %d # transferred %d \n", size_to_do, chunk_size, byte_transfered); + size_to_do -= chunk_size; /* subtract the quantity of data already transferred */ + } + + /* determine return code */ + if (byte_transfered != size) { + DEBUG_MSG("ERROR: SPI BURST READ FAILURE\n"); + return LGW_SPI_ERROR; + } else { + DEBUG_MSG("Note: SPI burst read success\n"); + return LGW_SPI_SUCCESS; + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/tst/test_loragw_cal.c b/libloragw/tst/test_loragw_cal.c new file mode 100644 index 0000000..533d189 --- /dev/null +++ b/libloragw/tst/test_loragw_cal.c @@ -0,0 +1,756 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Minimum test program for the loragw_hal 'library' + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +/* fix an issue between POSIX and C99 */ +#if __STDC_VERSION__ >= 199901L + #define _XOPEN_SOURCE 600 +#else + #define _XOPEN_SOURCE 500 +#endif + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ +#include <stdio.h> /* printf */ +#include <string.h> /* memset */ +#include <signal.h> /* sigaction */ +#include <math.h> /* cos */ +#include <unistd.h> /* getopt access */ + +#include "loragw_hal.h" +#include "loragw_reg.h" +#include "loragw_aux.h" +#include "loragw_radio.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +#define DEFAULT_TX_NOTCH_FREQ 129E3 +#define DEFAULT_RSSI_OFFSET 0.0 +#define NB_CAL_MAX 100 +#define MCU_AGC 1 +#define MCU_AGC_FW_BYTE 8192 /* size of the firmware IN BYTES (= twice the number of 14b words) */ +#define FW_VERSION_ADDR 0x20 +#define FW_VERSION_CAL 2 +#define RAM_SIZE 4096 +#define FREQ_SIG_NORM 0.078125 + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE VARIABLES ---------------------------------------------------- */ + +#include "../src/cal_fw.var" /* external definition of the variable */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE TYPES --------------------------------------------------------- */ + +struct cal_res_s { + int8_t amp_a; + int8_t phi_a; + int8_t amp_b; + int8_t phi_b; + int8_t offset_i_a [8]; + int8_t offset_q_a [8]; + int8_t offset_i_b [8]; + int8_t offset_q_b [8]; + uint8_t img_rej_a; + uint8_t img_rej_b; + uint8_t offset_rej_a [8]; + uint8_t offset_rej_b [8]; + uint8_t debug [8]; +}; + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ + +int load_firmware(uint8_t target, uint8_t *firmware, uint16_t size); /* defined in loragw_hal.c */ + +uint8_t sx125x_cal(uint8_t cal_cmd, struct cal_res_s *cal_res); + +int read_capture(int16_t *i, int16_t *q, int nb_samp); + +uint8_t get_img_rej(int16_t *sig_i, int16_t *sig_q, int nb_samp, double f_sig_norm); + +void usage (void); + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ + +/* describe command line options */ +void usage(void) { + printf("Library version information: %s\n", lgw_version_info()); + printf( "Available options:\n"); + printf( " -h print this help\n"); + printf( " -a <float> Radio A frequency in MHz\n"); + printf( " -b <float> Radio B frequency in MHz\n"); + printf( " -r <int> Radio type (SX1255:1255, SX1257:1257)\n"); + printf( " -n <uint> Number of calibration iterations\n"); + printf( " -k <int> Concentrator clock source (0:radio_A, 1:radio_B(default))\n"); + printf( " -t <int> Radio to run TX calibration on (0:None(default), 1:radio_A, 2:radio_B, 3:both)\n"); +} + +/* -------------------------------------------------------------------------- */ +/* --- MAIN FUNCTION -------------------------------------------------------- */ + +int main(int argc, char **argv) +{ + int i, j, x; + int32_t read_val; + struct lgw_conf_board_s boardconf; + struct lgw_conf_rxrf_s rfconf; + uint8_t fw_version; + uint8_t cal_cmd; + uint8_t cal_status; + struct cal_res_s cal_res [NB_CAL_MAX]; + struct cal_res_s cal_res_max; + struct cal_res_s cal_res_min; + int16_t sig_i [RAM_SIZE]; + int16_t sig_q [RAM_SIZE]; + uint8_t img_rej_a [NB_CAL_MAX]; + uint8_t img_rej_b [NB_CAL_MAX]; + uint8_t img_rej_a_max; + uint8_t img_rej_a_min; + uint8_t img_rej_b_max; + uint8_t img_rej_b_min; + //FILE *file; + + /* command line options */ + int xi = 0; + double xd = 0.0; + uint32_t fa = 0, fb = 0; + enum lgw_radio_type_e radio_type = LGW_RADIO_TYPE_NONE; + uint8_t clocksource = 1; /* Radio B is source by default */ + uint8_t tx_enable = 0; + int nb_cal = 5; + + /* parse command line options */ + while ((i = getopt (argc, argv, "ha:b:r:n:k:t:")) != -1) { + switch (i) { + case 'h': + usage(); + return -1; + break; + case 'a': /* <float> Radio A frequency in MHz */ + sscanf(optarg, "%lf", &xd); + fa = (uint32_t)((xd*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */ + break; + case 'b': /* <float> Radio B frequency in MHz */ + sscanf(optarg, "%lf", &xd); + fb = (uint32_t)((xd*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */ + break; + case 'r': /* <int> Radio type (1255, 1257) */ + sscanf(optarg, "%i", &xi); + switch (xi) { + case 1255: + radio_type = LGW_RADIO_TYPE_SX1255; + break; + case 1257: + radio_type = LGW_RADIO_TYPE_SX1257; + break; + default: + printf("ERROR: invalid radio type\n"); + usage(); + return -1; + } + break; + case 'n': /* <uint> Number of calibration iterations */ + i = sscanf(optarg, "%i", &xi); + if ((i != 1) || (xi > NB_CAL_MAX)) { + printf("ERROR: invalid number of calibration iterations (MAX %d)\n",NB_CAL_MAX); + usage(); + return -1; + } else { + nb_cal = xi; + } + break; + case 'k': /* <int> Concentrator clock source (Radio A or Radio B) */ + sscanf(optarg, "%i", &xi); + clocksource = (uint8_t)xi; + break; + case 't': /* <int> Radio to run TX calibration on */ + sscanf(optarg, "%i", &xi); + tx_enable = (uint8_t)xi; + break; + default: + printf("ERROR: argument parsing\n"); + usage(); + return -1; + } + } + + /* check input parameters */ + if ((fa == 0) || (fb == 0)) { + printf("ERROR: missing frequency input parameter:\n"); + printf(" Radio A RX: %u\n", fa); + printf(" Radio B RX: %u\n", fb); + usage(); + return -1; + } + + if (radio_type == LGW_RADIO_TYPE_NONE) { + printf("ERROR: missing radio type parameter:\n"); + usage(); + return -1; + } + + /* starting the concentrator */ + /* board config */ + memset(&boardconf, 0, sizeof(boardconf)); + + boardconf.lorawan_public = true; + boardconf.clksrc = clocksource; + lgw_board_setconf(boardconf); + + /* RF config */ + memset(&rfconf, 0, sizeof(rfconf)); + + rfconf.enable = true; + rfconf.freq_hz = fa; + rfconf.rssi_offset = DEFAULT_RSSI_OFFSET; + rfconf.type = radio_type; + rfconf.tx_enable = false; /* ignored */ + lgw_rxrf_setconf(0, rfconf); + + rfconf.freq_hz = fb; + rfconf.tx_enable = false; /* ignored */ + lgw_rxrf_setconf(1, rfconf); + + /* Calibration command */ + cal_cmd = 0; + //cal_cmd |= 0x01; /* Bit 0: Calibrate Rx IQ mismatch compensation on radio A */ + //cal_cmd |= 0x02; /* Bit 1: Calibrate Rx IQ mismatch compensation on radio B */ + //cal_cmd |= 0x04; /* Bit 2: Calibrate Tx DC offset on radio A */ + //cal_cmd |= 0x08; /* Bit 3: Calibrate Tx DC offset on radio B */ + cal_cmd |= 0x10; /* Bit 4: 0: calibrate with DAC gain=2, 1: with DAC gain=3 (use 3) */ + + switch (radio_type) { + case LGW_RADIO_TYPE_SX1255: + cal_cmd |= 0x20; /* Bit 5: 0: SX1257, 1: SX1255 */ + break; + case LGW_RADIO_TYPE_SX1257: + cal_cmd |= 0x00; /* Bit 5: 0: SX1257, 1: SX1255 */ + break; + default: + break; + } + + cal_cmd |= 0x00; /* Bit 6-7: Board type 0: ref, 1: FPGA, 3: board X */ + + /* Recap parameters*/ + printf("Library version information: %s\n", lgw_version_info()); + printf("Radio type: %d\n",radio_type); + printf("Radio A frequency: %f MHz\n",fa/1e6); + printf("Radio B frequency: %f MHz\n",fb/1e6); + printf("Number of calibration iterations: %d\n",nb_cal); + printf("Calibration command: brd: %d, chip: %d, dac: %d\n\n", cal_cmd >> 6, 1257-2*((cal_cmd & 0x20) >> 5), 2+((cal_cmd & 0x10) >> 4)); + + x = lgw_connect(false, DEFAULT_TX_NOTCH_FREQ); + if (x == -1) { + printf("ERROR: FAIL TO CONNECT BOARD\n"); + return -1; + } + + /* reset the registers (also shuts the radios down) */ + lgw_soft_reset(); + + /* ungate clocks (gated by default) */ + lgw_reg_w(LGW_GLOBAL_EN, 1); + + /* switch on and reset the radios (also starts the 32 MHz XTAL) */ + lgw_reg_w(LGW_RADIO_A_EN,1); + lgw_reg_w(LGW_RADIO_B_EN,1); + wait_ms(500); /* TODO: optimize */ + lgw_reg_w(LGW_RADIO_RST,1); + wait_ms(5); + lgw_reg_w(LGW_RADIO_RST,0); + + /* setup the radios */ + lgw_setup_sx125x(0, clocksource, true, radio_type, fa); + lgw_setup_sx125x(1, clocksource, false, radio_type, fb); + + /* Set GPIO 4 high for calibration */ + lgw_reg_w(LGW_GPIO_MODE,31); /* Set all GPIOs as output */ + lgw_reg_w(LGW_GPIO_SELECT_OUTPUT,2); /* AGC MCU drives GPIOs */ + + /* Load the calibration firmware */ + load_firmware(MCU_AGC, cal_firmware, MCU_AGC_FW_BYTE); + lgw_reg_w(LGW_MCU_RST_1,0); + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, FW_VERSION_ADDR); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + fw_version = (uint8_t)read_val; + if (fw_version != FW_VERSION_CAL) { + printf("ERROR: Version of calibration firmware not expected, actual:%d expected:%d\n", fw_version, FW_VERSION_CAL); + return -1; + } + + /* Run Rx A IQ mismatch calibration only */ + for (i=0; i<nb_cal; i++) { + cal_status = sx125x_cal(cal_cmd | 0x01, &cal_res[i]); + x = read_capture(sig_i, sig_q, RAM_SIZE); + /* + file = fopen("toto.txt","w"); + for (j=0; j<RAM_SIZE; j++) { + fprintf(file, "%d %d\n", sig_i[j], sig_q[j]); + } + fclose(file); + */ + img_rej_a[i] = get_img_rej(sig_i, sig_q, RAM_SIZE, FREQ_SIG_NORM); + + printf("Rx A IQ mismatch: Amp: %3d Phi: %3d Rej: %2d dB Status: %3d | Debug: Rej: %2d dB Lna: %1d BB: %2d Dec: %2d\n", cal_res[i].amp_a, cal_res[i].phi_a, cal_res[i].img_rej_a, cal_status, img_rej_a[i], cal_res[i].debug[0], cal_res[i].debug[1], cal_res[i].debug[2]); + } + + /* Run Rx B IQ mismatch calibation only */ + printf("\n"); + for (i=0; i<nb_cal; i++) { + cal_status = sx125x_cal(cal_cmd | 0x02, &cal_res[i]); + x = read_capture(sig_i, sig_q, RAM_SIZE); + img_rej_b[i] = get_img_rej(sig_i, sig_q, RAM_SIZE, FREQ_SIG_NORM); + + printf("Rx B IQ mismatch: Amp: %3d Phi: %3d Rej: %2d dB Status: %3d | Debug: Rej: %2d dB Lna: %1d BB: %2d Dec: %2d\n", cal_res[i].amp_b, cal_res[i].phi_b, cal_res[i].img_rej_b, cal_status, img_rej_b[i], cal_res[i].debug[0], cal_res[i].debug[1], cal_res[i].debug[2]); + } + + /* Run Tx A DC offset calibation only */ + printf("\n"); + if ((tx_enable == 1) || (tx_enable == 3)) { + for (i=0; i<nb_cal; i++) { + cal_status = sx125x_cal(cal_cmd | 0x04, &cal_res[i]); + + printf("Tx A DC offset I :"); + for (j=0; j<8; j++) { + printf(" %3d", cal_res[i].offset_i_a[j]); + } + printf("\n"); + printf("Tx A DC offset Q :"); + for (j=0; j<8; j++) { + printf(" %3d", cal_res[i].offset_q_a[j]); + } + printf("\n"); + printf("Tx A DC rejection:"); + for (j=0; j<8; j++) { + printf(" %3d", cal_res[i].offset_rej_a[j]); + } + printf("\n"); + printf("Tx A DC debug BB :"); + for (j=0; j<8; j++) { + printf(" %3d", (cal_res[i].debug[j] & 0xF0) >> 4); + } + printf("\n"); + printf("Tx A DC debug Dec:"); + for (j=0; j<8; j++) { + printf(" %3d", cal_res[i].debug[j] & 0x0F); + } + printf("\n"); + printf("Tx A DC Status : %3d\n", cal_status); + } + } else { + printf("Tx A calibration bypassed\n"); + } + + /* Run Tx B DC offset calibation only */ + printf("\n"); + if ((tx_enable == 2) || (tx_enable == 3)) { + for (i=0; i<nb_cal; i++) { + cal_status = sx125x_cal(cal_cmd | 0x08, &cal_res[i]); + + printf("Tx B DC offset I :"); + for (j=0; j<8; j++) { + printf(" %3d", cal_res[i].offset_i_b[j]); + } + printf("\n"); + printf("Tx B DC offset Q :"); + for (j=0; j<8; j++) { + printf(" %3d", cal_res[i].offset_q_b[j]); + } + printf("\n"); + printf("Tx B DC rejection:"); + for (j=0; j<8; j++) { + printf(" %3d", cal_res[i].offset_rej_b[j]); + } + printf("\n"); + printf("Tx B DC debug BB :"); + for (j=0; j<8; j++) { + printf(" %3d", (cal_res[i].debug[j] & 0xF0) >> 4); + } + printf("\n"); + printf("Tx B DC debug Dec:"); + for (j=0; j<8; j++) { + printf(" %3d", cal_res[i].debug[j] & 0x0F); + } + printf("\n"); + printf("Tx B DC Status : %3d\n", cal_status); + } + } else { + printf("Tx B calibration bypassed\n"); + } + + /* Compute statistics */ + cal_res_max.amp_a = -128; + cal_res_max.phi_a = -128; + cal_res_max.amp_b = -128; + cal_res_max.phi_b = -128; + cal_res_max.img_rej_a = 0; + cal_res_max.img_rej_b = 0; + for (j=0; j<8; j++) { + cal_res_max.offset_i_a[j] = -128; + cal_res_max.offset_q_a[j] = -128; + cal_res_max.offset_i_b[j] = -128; + cal_res_max.offset_q_b[j] = -128; + cal_res_max.offset_rej_a[j] = 0; + cal_res_max.offset_rej_b[j] = 0; + } + + cal_res_min.amp_a = 127; + cal_res_min.phi_a = 127; + cal_res_min.amp_b = 127; + cal_res_min.phi_b = 127; + cal_res_min.img_rej_a = 255; + cal_res_min.img_rej_b = 255; + for (j=0; j<8; j++) { + cal_res_min.offset_i_a[j] = 127; + cal_res_min.offset_q_a[j] = 127; + cal_res_min.offset_i_b[j] = 127; + cal_res_min.offset_q_b[j] = 127; + cal_res_min.offset_rej_a[j] = 255; + cal_res_min.offset_rej_b[j] = 255; + } + + img_rej_a_max = 0; + img_rej_a_min = 255; + img_rej_b_max = 0; + img_rej_b_min = 255; + + for (i=0; i<nb_cal; i++) { + if (cal_res[i].amp_a > cal_res_max.amp_a) { + cal_res_max.amp_a = cal_res[i].amp_a; + } + if (cal_res[i].phi_a > cal_res_max.phi_a) { + cal_res_max.phi_a = cal_res[i].phi_a; + } + if (cal_res[i].amp_b > cal_res_max.amp_b) { + cal_res_max.amp_b = cal_res[i].amp_b; + } + if (cal_res[i].phi_b > cal_res_max.phi_b) { + cal_res_max.phi_b = cal_res[i].phi_b; + } + if (cal_res[i].phi_b > cal_res_max.phi_b) { + cal_res_max.phi_b = cal_res[i].phi_b; + } + if (cal_res[i].img_rej_a > cal_res_max.img_rej_a) { + cal_res_max.img_rej_a = cal_res[i].img_rej_a; + } + if (cal_res[i].img_rej_b > cal_res_max.img_rej_b) { + cal_res_max.img_rej_b = cal_res[i].img_rej_b; + } + for (j=0; j<8; j++) { + if (cal_res[i].offset_i_a[j] > cal_res_max.offset_i_a[j]) { + cal_res_max.offset_i_a[j] = cal_res[i].offset_i_a[j]; + } + if (cal_res[i].offset_q_a[j] > cal_res_max.offset_q_a[j]) { + cal_res_max.offset_q_a[j] = cal_res[i].offset_q_a[j]; + } + if (cal_res[i].offset_i_b[j] > cal_res_max.offset_i_b[j]) { + cal_res_max.offset_i_b[j] = cal_res[i].offset_i_b[j]; + } + if (cal_res[i].offset_q_b[j] > cal_res_max.offset_q_b[j]) { + cal_res_max.offset_q_b[j] = cal_res[i].offset_q_b[j]; + } + if (cal_res[i].offset_rej_a[j] > cal_res_max.offset_rej_a[j]) { + cal_res_max.offset_rej_a[j] = cal_res[i].offset_rej_a[j]; + } + if (cal_res[i].offset_rej_b[j] > cal_res_max.offset_rej_b[j]) { + cal_res_max.offset_rej_b[j] = cal_res[i].offset_rej_b[j]; + } + } + + if (cal_res[i].amp_a < cal_res_min.amp_a) { + cal_res_min.amp_a = cal_res[i].amp_a; + } + if (cal_res[i].phi_a < cal_res_min.phi_a) { + cal_res_min.phi_a = cal_res[i].phi_a; + } + if (cal_res[i].amp_b < cal_res_min.amp_b) { + cal_res_min.amp_b = cal_res[i].amp_b; + } + if (cal_res[i].phi_b < cal_res_min.phi_b) { + cal_res_min.phi_b = cal_res[i].phi_b; + } + if (cal_res[i].phi_b < cal_res_min.phi_b) { + cal_res_min.phi_b = cal_res[i].phi_b; + } + if (cal_res[i].img_rej_a < cal_res_min.img_rej_a) { + cal_res_min.img_rej_a = cal_res[i].img_rej_a; + } + if (cal_res[i].img_rej_b < cal_res_min.img_rej_b) { + cal_res_min.img_rej_b = cal_res[i].img_rej_b; + } + for (j=0; j<8; j++) { + if (cal_res[i].offset_i_a[j] < cal_res_min.offset_i_a[j]) { + cal_res_min.offset_i_a[j] = cal_res[i].offset_i_a[j]; + } + if (cal_res[i].offset_q_a[j] < cal_res_min.offset_q_a[j]) { + cal_res_min.offset_q_a[j] = cal_res[i].offset_q_a[j]; + } + if (cal_res[i].offset_i_b[j] < cal_res_min.offset_i_b[j]) { + cal_res_min.offset_i_b[j] = cal_res[i].offset_i_b[j]; + } + if (cal_res[i].offset_q_b[j] < cal_res_min.offset_q_b[j]) { + cal_res_min.offset_q_b[j] = cal_res[i].offset_q_b[j]; + } + if (cal_res[i].offset_rej_a[j] < cal_res_min.offset_rej_a[j]) { + cal_res_min.offset_rej_a[j] = cal_res[i].offset_rej_a[j]; + } + if (cal_res[i].offset_rej_b[j] < cal_res_min.offset_rej_b[j]) { + cal_res_min.offset_rej_b[j] = cal_res[i].offset_rej_b[j]; + } + } + + if (img_rej_a[i] > img_rej_a_max) { + img_rej_a_max = img_rej_a[i]; + } + if (img_rej_a[i] < img_rej_a_min) { + img_rej_a_min = img_rej_a[i]; + } + if (img_rej_b[i] > img_rej_b_max) { + img_rej_b_max = img_rej_b[i]; + } + if (img_rej_b[i] < img_rej_b_min) { + img_rej_b_min = img_rej_b[i]; + } + } + + /* Print statistics */ + printf("\n"); + printf("Rx A IQ mismatch calibration statistics on %3d iterations (min, max):\n", nb_cal); + printf("Amp: %3d %3d Phi: %3d %3d Rej: %2d %2d dB (capt.: %2d %2d dB)\n", cal_res_min.amp_a, cal_res_max.amp_a, cal_res_min.phi_a, cal_res_max.phi_a, cal_res_min.img_rej_a, cal_res_max.img_rej_a, img_rej_a_min, img_rej_a_max); + + printf("\n"); + printf("Rx B IQ mismatch calibration statistics on %3d iterations (min, max):\n", nb_cal); + printf("Amp: %3d %3d Phi: %3d %3d Rej: %2d %2d dB (capt.: %2d %2d dB)\n", cal_res_min.amp_b, cal_res_max.amp_b, cal_res_min.phi_b, cal_res_max.phi_b, cal_res_min.img_rej_b, cal_res_max.img_rej_b, img_rej_b_min, img_rej_b_max); + + if ((tx_enable == 1) || (tx_enable == 3)) { + printf("\n"); + printf("Tx A DC offset calibration statistics on %3d iterations (min, max):\n", nb_cal); + for (j=0; j<8; j++) { + printf(" Mix gain %2d: I: %3d %3d Q: %3d %3d Rej: %2d %2d dB\n", 8+j, cal_res_min.offset_i_a[j], cal_res_max.offset_i_a[j], cal_res_min.offset_q_a[j], cal_res_max.offset_q_a[j], cal_res_min.offset_rej_a[j], cal_res_max.offset_rej_a[j]); + } + } + + if ((tx_enable == 2) || (tx_enable == 3)) { + printf("\n"); + printf("Tx B DC offset calibration statistics on %3d iterations (min, max):\n", nb_cal); + for (j=0; j<8; j++) { + printf(" Mix gain %2d: I: %3d %3d Q: %3d %3d Rej: %2d %2d dB\n", 8+j, cal_res_min.offset_i_b[j], cal_res_max.offset_i_b[j], cal_res_min.offset_q_b[j], cal_res_max.offset_q_b[j], cal_res_min.offset_rej_b[j], cal_res_max.offset_rej_b[j]); + } + } + + lgw_stop(); + + printf("\nEnd of radio calibration test\n"); + + return 0; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +uint8_t sx125x_cal(uint8_t cal_cmd, struct cal_res_s *cal_res) { + + int i; + int32_t read_val; + uint8_t cal_status; + + lgw_reg_w(LGW_FORCE_HOST_RADIO_CTRL,0); /* gives to AGC MCU the control of the radios */ + lgw_reg_w(LGW_RADIO_SELECT,cal_cmd); /* send calibration configuration word */ + lgw_reg_w(LGW_MCU_RST_1,1); + lgw_reg_w(LGW_MCU_RST_1,0); + lgw_reg_w(LGW_PAGE_REG,3); /* Calibration will start on this condition as soon as MCU can talk to concentrator registers */ + lgw_reg_w(LGW_EMERGENCY_FORCE_HOST_CTRL,0); /* Give control of concentrator registers to MCU */ + + wait_ms(2000); /* Wait for end of calibration */ + + lgw_reg_w(LGW_EMERGENCY_FORCE_HOST_CTRL,1); /* Take back control */ + + /* Get calibration status */ + lgw_reg_r(LGW_MCU_AGC_STATUS, &read_val); + cal_status = (uint8_t)read_val; + + /* Check calibration flags + bit 0: could access SX1301 registers + bit 1: could access radio A registers + bit 2: could access radio B registers + bit 3: radio A RX image rejection successful + bit 4: radio B RX image rejection successful + bit 5: radio A TX imbalance correction successful + bit 6: radio B TX imbalance correction successful + bit 7: calibration finished */ + + if ((cal_status & 0x01) == 0) { + printf("WARNING: calibration could not access SX1301 registers\n"); + } + if ((cal_status & 0x02) == 0) { + printf("WARNING: calibration could not access radio A\n"); + } + if ((cal_status & 0x04) == 0) { + printf("WARNING: calibration could not access radio B\n"); + } + if ((cal_cmd & 0x01) && ((cal_status & 0x08) == 0)) { + printf("WARNING: problem in calibration of radio A for image rejection\n"); + } + if ((cal_cmd & 0x02) && ((cal_status & 0x10) == 0)) { + printf("WARNING: problem in calibration of radio B for image rejection\n"); + } + if ((cal_cmd & 0x04) && ((cal_status & 0x20) == 0)) { + printf("WARNING: problem in calibration of radio A for TX imbalance\n"); + } + if ((cal_cmd & 0x08) && ((cal_status & 0x40) == 0)) { + printf("WARNING: problem in calibration of radio B for TX imbalance\n"); + } + if ((cal_status & 0x80) == 0) { + printf("WARNING: Calibration not finished\n"); + } + + /* Get calibration results */ + if (cal_cmd & 0x01) { + lgw_reg_r(LGW_IQ_MISMATCH_A_AMP_COEFF, &read_val); + (*cal_res).amp_a = (int8_t)((read_val > 31) ? read_val - 64 : read_val); + lgw_reg_r(LGW_IQ_MISMATCH_A_PHI_COEFF, &read_val); + (*cal_res).phi_a = (int8_t)((read_val > 31) ? read_val - 64 : read_val); + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD0); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).img_rej_a = (uint8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD2); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).debug[0] = (uint8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD3); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).debug[1] = (uint8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD4); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).debug[2] = (uint8_t)read_val; + } + if (cal_cmd & 0x02) { + lgw_reg_r(LGW_IQ_MISMATCH_B_AMP_COEFF, &read_val); + (*cal_res).amp_b = (int8_t)((read_val > 31) ? read_val - 64 : read_val); + lgw_reg_r(LGW_IQ_MISMATCH_B_PHI_COEFF, &read_val); + (*cal_res).phi_b = (int8_t)((read_val > 31) ? read_val - 64 : read_val); + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD1); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).img_rej_b = (uint8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD2); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).debug[0] = (uint8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD3); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).debug[1] = (uint8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD4); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).debug[2] = (uint8_t)read_val; + } + if (cal_cmd & 0x04) { + for (i=0; i<=7; ++i) { + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xA0+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).offset_i_a[i] = (int8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xA8+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).offset_q_a[i] = (int8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xC0+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).offset_rej_a[i] = (uint8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD2+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).debug[i] = (uint8_t)read_val; + } + } + if (cal_cmd & 0x08) { + for (i=0; i<=7; ++i) { + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xB0+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).offset_i_b[i] = (int8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xB8+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).offset_q_b[i] = (int8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xC8+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).offset_rej_b[i] = (uint8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD2+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).debug[i] = (uint8_t)read_val; + } + } + + return cal_status; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int read_capture(int16_t *sig_i, int16_t *sig_q, int nb_samp) { + + uint8_t read_burst[4]; + uint16_t data_i_c2; + uint16_t data_q_c2; + int i; + + lgw_reg_w(LGW_CAPTURE_RAM_ADDR, 0); + for (i=0 ; i<nb_samp ; i++) { + lgw_reg_rb(LGW_CAPTURE_RAM_DATA, read_burst, 4); + data_i_c2 = ((uint16_t)read_burst[3] << 4) + ((uint16_t)read_burst[2] >> 4); + data_q_c2 = ((uint16_t)read_burst[1] << 4) + ((uint16_t)read_burst[0] >> 4); + sig_i[i] = (int16_t)((data_i_c2 > 2047) ? data_i_c2 - 4096 : data_i_c2); + sig_q[i] = (int16_t)((data_q_c2 > 2047) ? data_q_c2 - 4096 : data_q_c2); + } + + return 0; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +uint8_t get_img_rej(int16_t *sig_i, int16_t *sig_q, int nb_samp, double f_sig_norm) { + + int i; + double phase; + double corr_sig_i, corr_sig_q, corr_sig_abs; + double corr_img_i, corr_img_q, corr_img_abs; + double img_rej; + + corr_sig_i = 0; + corr_sig_q = 0; + corr_img_i = 0; + corr_img_q = 0; + + for (i=0 ; i<nb_samp ; i++) { + phase = 6.28318530717959*i*f_sig_norm; + corr_sig_i += (double)sig_i[i]*cos( phase) - (double)sig_q[i]*sin( phase); + corr_sig_q += (double)sig_q[i]*cos( phase) + (double)sig_i[i]*sin( phase); + corr_img_i += (double)sig_i[i]*cos(-phase) - (double)sig_q[i]*sin(-phase); + corr_img_q += (double)sig_q[i]*cos(-phase) + (double)sig_i[i]*sin(-phase); + } + + corr_sig_abs = sqrt( corr_sig_i*corr_sig_i + corr_sig_q*corr_sig_q ); + corr_img_abs = sqrt( corr_img_i*corr_img_i + corr_img_q*corr_img_q ); + + img_rej = 20*log10(corr_sig_abs/corr_img_abs); + + return (uint8_t)img_rej; +} + + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/tst/test_loragw_gps.c b/libloragw/tst/test_loragw_gps.c new file mode 100644 index 0000000..a4164a3 --- /dev/null +++ b/libloragw/tst/test_loragw_gps.c @@ -0,0 +1,288 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Minimum test program for the loragw_gps 'library' + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Michael Coracin +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +/* fix an issue between POSIX and C99 */ +#if __STDC_VERSION__ >= 199901L + #define _XOPEN_SOURCE 600 +#else + #define _XOPEN_SOURCE 500 +#endif + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ +#include <stdio.h> /* printf */ +#include <string.h> /* memset */ +#include <signal.h> /* sigaction */ +#include <stdlib.h> /* exit */ +#include <unistd.h> /* read */ + +#include "loragw_hal.h" +#include "loragw_gps.h" +#include "loragw_aux.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE VARIABLES ---------------------------------------------------- */ + +static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */ +static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */ + +struct tref ppm_ref; + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ + +static void sig_handler(int sigio); +static void gps_process_sync(void); +static void gps_process_coords(void); + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ + +static void sig_handler(int sigio) { + if (sigio == SIGQUIT) { + quit_sig = 1;; + } else if ((sigio == SIGINT) || (sigio == SIGTERM)) { + exit_sig = 1; + } +} + +static void gps_process_sync(void) { + /* variables for PPM pulse GPS synchronization */ + uint32_t ppm_tstamp; + struct timespec ppm_gps; + struct timespec ppm_utc; + + /* variables for timestamp <-> GPS time conversions */ + uint32_t x, z; + struct timespec y; + + /* get GPS time for synchronization */ + int i = lgw_gps_get(&ppm_utc, &ppm_gps, NULL, NULL); + if (i != LGW_GPS_SUCCESS) { + printf(" No valid reference GPS time available, synchronization impossible.\n"); + return; + } + + /* get timestamp for synchronization */ + i = lgw_get_trigcnt(&ppm_tstamp); + if (i != LGW_HAL_SUCCESS) { + printf(" Failed to read timestamp, synchronization impossible.\n"); + return; + } + + /* try to update synchronize time reference with the new GPS & timestamp */ + i = lgw_gps_sync(&ppm_ref, ppm_tstamp, ppm_utc, ppm_gps); + if (i != LGW_GPS_SUCCESS) { + printf(" Synchronization error.\n"); + return; + } + + /* display result */ + printf(" * Synchronization successful *\n"); + printf(" UTC reference time: %lld.%09ld\n", (long long)ppm_ref.utc.tv_sec, ppm_ref.utc.tv_nsec); + printf(" GPS reference time: %lld.%09ld\n", (long long)ppm_ref.gps.tv_sec, ppm_ref.gps.tv_nsec); + printf(" Internal counter reference value: %u\n", ppm_ref.count_us); + printf(" Clock error: %.9f\n", ppm_ref.xtal_err); + + x = ppm_tstamp + 500000; + printf(" * Test of timestamp counter <-> GPS value conversion *\n"); + printf(" Test value: %u\n", x); + lgw_cnt2gps(ppm_ref, x, &y); + printf(" Conversion to GPS: %lld.%09ld\n", (long long)y.tv_sec, y.tv_nsec); + lgw_gps2cnt(ppm_ref, y, &z); + printf(" Converted back: %u ==> %dµs\n", z, (int32_t)(z-x)); + printf(" * Test of timestamp counter <-> UTC value conversion *\n"); + printf(" Test value: %u\n", x); + lgw_cnt2utc(ppm_ref, x, &y); + printf(" Conversion to UTC: %lld.%09ld\n", (long long)y.tv_sec, y.tv_nsec); + lgw_utc2cnt(ppm_ref, y, &z); + printf(" Converted back: %u ==> %dµs\n", z, (int32_t)(z-x)); +} + +static void gps_process_coords(void) { + /* position variable */ + struct coord_s coord; + struct coord_s gpserr; + int i = lgw_gps_get(NULL, NULL, &coord, &gpserr); + + /* update gateway coordinates */ + if (i == LGW_GPS_SUCCESS) { + printf("# GPS coordinates: latitude %.5f, longitude %.5f, altitude %i m\n", coord.lat, coord.lon, coord.alt); + printf("# GPS err: latitude %.5f, longitude %.5f, altitude %i m\n", gpserr.lat, gpserr.lon, gpserr.alt); + } +} + +/* -------------------------------------------------------------------------- */ +/* --- MAIN FUNCTION -------------------------------------------------------- */ + +int main() +{ + struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */ + + int i; + + /* concentrator variables */ + struct lgw_conf_board_s boardconf; + struct lgw_conf_rxrf_s rfconf; + + /* serial variables */ + char serial_buff[128]; /* buffer to receive GPS data */ + size_t wr_idx = 0; /* pointer to end of chars in buffer */ + int gps_tty_dev; /* file descriptor to the serial port of the GNSS module */ + + /* NMEA/UBX variables */ + enum gps_msg latest_msg; /* keep track of latest NMEA/UBX message parsed */ + + /* configure signal handling */ + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_handler = sig_handler; + sigaction(SIGQUIT, &sigact, NULL); + sigaction(SIGINT, &sigact, NULL); + sigaction(SIGTERM, &sigact, NULL); + + /* Intro message and library information */ + printf("Beginning of test for loragw_gps.c\n"); + printf("*** Library version information ***\n%s\n***\n", lgw_version_info()); + + /* Open and configure GPS */ + i = lgw_gps_enable("/dev/ttyAMA0", "ubx7", 0, &gps_tty_dev); + if (i != LGW_GPS_SUCCESS) { + printf("ERROR: IMPOSSIBLE TO ENABLE GPS\n"); + exit(EXIT_FAILURE); + } + + /* start concentrator (default conf for IoT SK) */ + /* board config */ + memset(&boardconf, 0, sizeof(boardconf)); + boardconf.lorawan_public = true; + boardconf.clksrc = 1; + lgw_board_setconf(boardconf); + + /* RF config */ + memset(&rfconf, 0, sizeof(rfconf)); + rfconf.enable = true; + rfconf.freq_hz = 868000000; + rfconf.rssi_offset = 0.0; + rfconf.type = LGW_RADIO_TYPE_SX1257; + rfconf.tx_enable = true; + lgw_rxrf_setconf(0, rfconf); + + lgw_start(); + + /* initialize some variables before loop */ + memset(serial_buff, 0, sizeof serial_buff); + memset(&ppm_ref, 0, sizeof ppm_ref); + + /* loop until user action */ + while ((quit_sig != 1) && (exit_sig != 1)) { + size_t rd_idx = 0; + size_t frame_end_idx = 0; + + /* blocking non-canonical read on serial port */ + ssize_t nb_char = read(gps_tty_dev, serial_buff + wr_idx, LGW_GPS_MIN_MSG_SIZE); + if (nb_char <= 0) { + printf("WARNING: [gps] read() returned value %d\n", nb_char); + continue; + } + wr_idx += (size_t)nb_char; + + /******************************************* + * Scan buffer for UBX/NMEA sync chars and * + * attempt to decode frame if one is found * + *******************************************/ + while (rd_idx < wr_idx) { + size_t frame_size = 0; + + /* Scan buffer for UBX sync char */ + if (serial_buff[rd_idx] == LGW_GPS_UBX_SYNC_CHAR) { + + /*********************** + * Found UBX sync char * + ***********************/ + latest_msg = lgw_parse_ubx(&serial_buff[rd_idx], (wr_idx - rd_idx), &frame_size); + + if (frame_size > 0) { + if (latest_msg == INCOMPLETE) { + /* UBX header found but frame appears to be missing bytes */ + frame_size = 0; + } else if (latest_msg == INVALID) { + /* message header received but message appears to be corrupted */ + printf("WARNING: [gps] could not get a valid message from GPS (no time)\n"); + frame_size = 0; + } else if (latest_msg == UBX_NAV_TIMEGPS) { + printf("\n~~ UBX NAV-TIMEGPS sentence, triggering synchronization attempt ~~\n"); + gps_process_sync(); + } + } + } else if(serial_buff[rd_idx] == LGW_GPS_NMEA_SYNC_CHAR) { + /************************ + * Found NMEA sync char * + ************************/ + /* scan for NMEA end marker (LF = 0x0a) */ + char* nmea_end_ptr = memchr(&serial_buff[rd_idx],(int)0x0a, (wr_idx - rd_idx)); + + if (nmea_end_ptr) { + /* found end marker */ + frame_size = nmea_end_ptr - &serial_buff[rd_idx] + 1; + latest_msg = lgw_parse_nmea(&serial_buff[rd_idx], frame_size); + + if(latest_msg == INVALID || latest_msg == UNKNOWN) { + /* checksum failed */ + frame_size = 0; + } else if (latest_msg == NMEA_RMC) { /* Get location from RMC frames */ + gps_process_coords(); + } + } + } + + if (frame_size > 0) { + /* At this point message is a checksum verified frame + we're processed or ignored. Remove frame from buffer */ + rd_idx += frame_size; + frame_end_idx = rd_idx; + } else { + rd_idx++; + } + } /* ...for(rd_idx = 0... */ + + if (frame_end_idx) { + /* Frames have been processed. Remove bytes to end of last processed frame */ + memcpy(serial_buff,&serial_buff[frame_end_idx],wr_idx - frame_end_idx); + wr_idx -= frame_end_idx; + } /* ...for(rd_idx = 0... */ + + /* Prevent buffer overflow */ + if ((sizeof(serial_buff) - wr_idx) < LGW_GPS_MIN_MSG_SIZE) { + memcpy(serial_buff,&serial_buff[LGW_GPS_MIN_MSG_SIZE],wr_idx - LGW_GPS_MIN_MSG_SIZE); + wr_idx -= LGW_GPS_MIN_MSG_SIZE; + } + } + + /* clean up before leaving */ + if (exit_sig == 1) { + lgw_gps_disable(gps_tty_dev); + lgw_stop(); + } + + printf("\nEnd of test for loragw_gps.c\n"); + exit(EXIT_SUCCESS); +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/tst/test_loragw_hal.c b/libloragw/tst/test_loragw_hal.c new file mode 100644 index 0000000..e2fee5e --- /dev/null +++ b/libloragw/tst/test_loragw_hal.c @@ -0,0 +1,416 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Minimum test program for the loragw_hal 'library' + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +/* fix an issue between POSIX and C99 */ +#if __STDC_VERSION__ >= 199901L + #define _XOPEN_SOURCE 600 +#else + #define _XOPEN_SOURCE 500 +#endif + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ +#include <stdio.h> /* printf */ +#include <string.h> /* memset */ +#include <signal.h> /* sigaction */ +#include <unistd.h> /* getopt access */ + +#include "loragw_hal.h" +#include "loragw_reg.h" +#include "loragw_aux.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +#define DEFAULT_RSSI_OFFSET 0.0 +#define DEFAULT_NOTCH_FREQ 129000U + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE VARIABLES ---------------------------------------------------- */ + +static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */ +static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ + +static void sig_handler(int sigio); + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ + +static void sig_handler(int sigio) { + if (sigio == SIGQUIT) { + quit_sig = 1;; + } else if ((sigio == SIGINT) || (sigio == SIGTERM)) { + exit_sig = 1; + } +} + +/* describe command line options */ +void usage(void) { + printf("Library version information: %s\n", lgw_version_info()); + printf( "Available options:\n"); + printf( " -h print this help\n"); + printf( " -a <float> Radio A RX frequency in MHz\n"); + printf( " -b <float> Radio B RX frequency in MHz\n"); + printf( " -t <float> Radio TX frequency in MHz\n"); + printf( " -r <int> Radio type (SX1255:1255, SX1257:1257)\n"); + printf( " -k <int> Concentrator clock source (0: radio_A, 1: radio_B(default))\n"); +} + +/* -------------------------------------------------------------------------- */ +/* --- MAIN FUNCTION -------------------------------------------------------- */ + +int main(int argc, char **argv) +{ + struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */ + + struct lgw_conf_board_s boardconf; + struct lgw_conf_rxrf_s rfconf; + struct lgw_conf_rxif_s ifconf; + + struct lgw_pkt_rx_s rxpkt[4]; /* array containing up to 4 inbound packets metadata */ + struct lgw_pkt_tx_s txpkt; /* configuration and metadata for an outbound packet */ + struct lgw_pkt_rx_s *p; /* pointer on a RX packet */ + + int i, j; + int nb_pkt; + uint32_t fa = 0, fb = 0, ft = 0; + enum lgw_radio_type_e radio_type = LGW_RADIO_TYPE_NONE; + uint8_t clocksource = 1; /* Radio B is source by default */ + + uint32_t tx_cnt = 0; + unsigned long loop_cnt = 0; + uint8_t status_var = 0; + double xd = 0.0; + int xi = 0; + + /* parse command line options */ + while ((i = getopt (argc, argv, "ha:b:t:r:k:")) != -1) { + switch (i) { + case 'h': + usage(); + return -1; + break; + case 'a': /* <float> Radio A RX frequency in MHz */ + sscanf(optarg, "%lf", &xd); + fa = (uint32_t)((xd*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */ + break; + case 'b': /* <float> Radio B RX frequency in MHz */ + sscanf(optarg, "%lf", &xd); + fb = (uint32_t)((xd*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */ + break; + case 't': /* <float> Radio TX frequency in MHz */ + sscanf(optarg, "%lf", &xd); + ft = (uint32_t)((xd*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */ + break; + case 'r': /* <int> Radio type (1255, 1257) */ + sscanf(optarg, "%i", &xi); + switch (xi) { + case 1255: + radio_type = LGW_RADIO_TYPE_SX1255; + break; + case 1257: + radio_type = LGW_RADIO_TYPE_SX1257; + break; + default: + printf("ERROR: invalid radio type\n"); + usage(); + return -1; + } + break; + case 'k': /* <int> Concentrator clock source (Radio A or Radio B) */ + sscanf(optarg, "%i", &xi); + clocksource = (uint8_t)xi; + break; + default: + printf("ERROR: argument parsing\n"); + usage(); + return -1; + } + } + + /* check input parameters */ + if ((fa == 0) || (fb == 0) || (ft == 0)) { + printf("ERROR: missing frequency input parameter:\n"); + printf(" Radio A RX: %u\n", fa); + printf(" Radio B RX: %u\n", fb); + printf(" Radio TX: %u\n", ft); + usage(); + return -1; + } + + if (radio_type == LGW_RADIO_TYPE_NONE) { + printf("ERROR: missing radio type parameter:\n"); + usage(); + return -1; + } + + /* configure signal handling */ + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_handler = sig_handler; + sigaction(SIGQUIT, &sigact, NULL); + sigaction(SIGINT, &sigact, NULL); + sigaction(SIGTERM, &sigact, NULL); + + /* beginning of LoRa concentrator-specific code */ + printf("Beginning of test for loragw_hal.c\n"); + + printf("*** Library version information ***\n%s\n\n", lgw_version_info()); + + /* set configuration for board */ + memset(&boardconf, 0, sizeof(boardconf)); + + boardconf.lorawan_public = true; + boardconf.clksrc = clocksource; + lgw_board_setconf(boardconf); + + /* set configuration for RF chains */ + memset(&rfconf, 0, sizeof(rfconf)); + + rfconf.enable = true; + rfconf.freq_hz = fa; + rfconf.rssi_offset = DEFAULT_RSSI_OFFSET; + rfconf.type = radio_type; + rfconf.tx_enable = true; + rfconf.tx_notch_freq = DEFAULT_NOTCH_FREQ; + lgw_rxrf_setconf(0, rfconf); /* radio A, f0 */ + + rfconf.enable = true; + rfconf.freq_hz = fb; + rfconf.rssi_offset = DEFAULT_RSSI_OFFSET; + rfconf.type = radio_type; + rfconf.tx_enable = false; + lgw_rxrf_setconf(1, rfconf); /* radio B, f1 */ + + /* set configuration for LoRa multi-SF channels (bandwidth cannot be set) */ + memset(&ifconf, 0, sizeof(ifconf)); + + ifconf.enable = true; + ifconf.rf_chain = 1; + ifconf.freq_hz = -400000; + ifconf.datarate = DR_LORA_MULTI; + lgw_rxif_setconf(0, ifconf); /* chain 0: LoRa 125kHz, all SF, on f1 - 0.4 MHz */ + + ifconf.enable = true; + ifconf.rf_chain = 1; + ifconf.freq_hz = -200000; + ifconf.datarate = DR_LORA_MULTI; + lgw_rxif_setconf(1, ifconf); /* chain 1: LoRa 125kHz, all SF, on f1 - 0.2 MHz */ + + ifconf.enable = true; + ifconf.rf_chain = 1; + ifconf.freq_hz = 0; + ifconf.datarate = DR_LORA_MULTI; + lgw_rxif_setconf(2, ifconf); /* chain 2: LoRa 125kHz, all SF, on f1 - 0.0 MHz */ + + ifconf.enable = true; + ifconf.rf_chain = 0; + ifconf.freq_hz = -400000; + ifconf.datarate = DR_LORA_MULTI; + lgw_rxif_setconf(3, ifconf); /* chain 3: LoRa 125kHz, all SF, on f0 - 0.4 MHz */ + + ifconf.enable = true; + ifconf.rf_chain = 0; + ifconf.freq_hz = -200000; + ifconf.datarate = DR_LORA_MULTI; + lgw_rxif_setconf(4, ifconf); /* chain 4: LoRa 125kHz, all SF, on f0 - 0.2 MHz */ + + ifconf.enable = true; + ifconf.rf_chain = 0; + ifconf.freq_hz = 0; + ifconf.datarate = DR_LORA_MULTI; + lgw_rxif_setconf(5, ifconf); /* chain 5: LoRa 125kHz, all SF, on f0 + 0.0 MHz */ + + ifconf.enable = true; + ifconf.rf_chain = 0; + ifconf.freq_hz = 200000; + ifconf.datarate = DR_LORA_MULTI; + lgw_rxif_setconf(6, ifconf); /* chain 6: LoRa 125kHz, all SF, on f0 + 0.2 MHz */ + + ifconf.enable = true; + ifconf.rf_chain = 0; + ifconf.freq_hz = 400000; + ifconf.datarate = DR_LORA_MULTI; + lgw_rxif_setconf(7, ifconf); /* chain 7: LoRa 125kHz, all SF, on f0 + 0.4 MHz */ + + /* set configuration for LoRa 'stand alone' channel */ + memset(&ifconf, 0, sizeof(ifconf)); + ifconf.enable = true; + ifconf.rf_chain = 0; + ifconf.freq_hz = 0; + ifconf.bandwidth = BW_250KHZ; + ifconf.datarate = DR_LORA_SF10; + lgw_rxif_setconf(8, ifconf); /* chain 8: LoRa 250kHz, SF10, on f0 MHz */ + + /* set configuration for FSK channel */ + memset(&ifconf, 0, sizeof(ifconf)); + ifconf.enable = true; + ifconf.rf_chain = 1; + ifconf.freq_hz = 0; + ifconf.bandwidth = BW_250KHZ; + ifconf.datarate = 64000; + lgw_rxif_setconf(9, ifconf); /* chain 9: FSK 64kbps, on f1 MHz */ + + /* set configuration for TX packet */ + memset(&txpkt, 0, sizeof(txpkt)); + txpkt.freq_hz = ft; + txpkt.tx_mode = IMMEDIATE; + txpkt.rf_power = 10; + txpkt.modulation = MOD_LORA; + txpkt.bandwidth = BW_125KHZ; + txpkt.datarate = DR_LORA_SF9; + txpkt.coderate = CR_LORA_4_5; + strcpy((char *)txpkt.payload, "TX.TEST.LORA.GW.????" ); + txpkt.size = 20; + txpkt.preamble = 6; + txpkt.rf_chain = 0; +/* + memset(&txpkt, 0, sizeof(txpkt)); + txpkt.freq_hz = F_TX; + txpkt.tx_mode = IMMEDIATE; + txpkt.rf_power = 10; + txpkt.modulation = MOD_FSK; + txpkt.f_dev = 50; + txpkt.datarate = 64000; + strcpy((char *)txpkt.payload, "TX.TEST.LORA.GW.????" ); + txpkt.size = 20; + txpkt.preamble = 4; + txpkt.rf_chain = 0; +*/ + + /* connect, configure and start the LoRa concentrator */ + i = lgw_start(); + if (i == LGW_HAL_SUCCESS) { + printf("*** Concentrator started ***\n"); + } else { + printf("*** Impossible to start concentrator ***\n"); + return -1; + } + + /* once configured, dump content of registers to a file, for reference */ + // FILE * reg_dump = NULL; + // reg_dump = fopen("reg_dump.log", "w"); + // if (reg_dump != NULL) { + // lgw_reg_check(reg_dump); + // fclose(reg_dump); + // } + + while ((quit_sig != 1) && (exit_sig != 1)) { + loop_cnt++; + + /* fetch N packets */ + nb_pkt = lgw_receive(ARRAY_SIZE(rxpkt), rxpkt); + + if (nb_pkt == 0) { + wait_ms(300); + } else { + /* display received packets */ + for(i=0; i < nb_pkt; ++i) { + p = &rxpkt[i]; + printf("---\nRcv pkt #%d >>", i+1); + if (p->status == STAT_CRC_OK) { + printf(" if_chain:%2d", p->if_chain); + printf(" tstamp:%010u", p->count_us); + printf(" size:%3u", p->size); + switch (p-> modulation) { + case MOD_LORA: printf(" LoRa"); break; + case MOD_FSK: printf(" FSK"); break; + default: printf(" modulation?"); + } + switch (p->datarate) { + case DR_LORA_SF7: printf(" SF7"); break; + case DR_LORA_SF8: printf(" SF8"); break; + case DR_LORA_SF9: printf(" SF9"); break; + case DR_LORA_SF10: printf(" SF10"); break; + case DR_LORA_SF11: printf(" SF11"); break; + case DR_LORA_SF12: printf(" SF12"); break; + default: printf(" datarate?"); + } + switch (p->coderate) { + case CR_LORA_4_5: printf(" CR1(4/5)"); break; + case CR_LORA_4_6: printf(" CR2(2/3)"); break; + case CR_LORA_4_7: printf(" CR3(4/7)"); break; + case CR_LORA_4_8: printf(" CR4(1/2)"); break; + default: printf(" coderate?"); + } + printf("\n"); + printf(" RSSI:%+6.1f SNR:%+5.1f (min:%+5.1f, max:%+5.1f) payload:\n", p->rssi, p->snr, p->snr_min, p->snr_max); + + for (j = 0; j < p->size; ++j) { + printf(" %02X", p->payload[j]); + } + printf(" #\n"); + } else if (p->status == STAT_CRC_BAD) { + printf(" if_chain:%2d", p->if_chain); + printf(" tstamp:%010u", p->count_us); + printf(" size:%3u\n", p->size); + printf(" CRC error, damaged packet\n\n"); + } else if (p->status == STAT_NO_CRC){ + printf(" if_chain:%2d", p->if_chain); + printf(" tstamp:%010u", p->count_us); + printf(" size:%3u\n", p->size); + printf(" no CRC\n\n"); + } else { + printf(" if_chain:%2d", p->if_chain); + printf(" tstamp:%010u", p->count_us); + printf(" size:%3u\n", p->size); + printf(" invalid status ?!?\n\n"); + } + } + } + + /* send a packet every X loop */ + if (loop_cnt%16 == 0) { + /* 32b counter in the payload, big endian */ + txpkt.payload[16] = 0xff & (tx_cnt >> 24); + txpkt.payload[17] = 0xff & (tx_cnt >> 16); + txpkt.payload[18] = 0xff & (tx_cnt >> 8); + txpkt.payload[19] = 0xff & tx_cnt; + i = lgw_send(txpkt); /* non-blocking scheduling of TX packet */ + j = 0; + printf("+++\nSending packet #%d, rf path %d, return %d\nstatus -> ", tx_cnt, txpkt.rf_chain, i); + do { + ++j; + wait_ms(100); + lgw_status(TX_STATUS, &status_var); /* get TX status */ + printf("%d:", status_var); + } while ((status_var != TX_FREE) && (j < 100)); + ++tx_cnt; + printf("\nTX finished\n"); + } + } + + if (exit_sig == 1) { + /* clean up before leaving */ + lgw_stop(); + } + + printf("\nEnd of test for loragw_hal.c\n"); + return 0; +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/tst/test_loragw_reg.c b/libloragw/tst/test_loragw_reg.c new file mode 100644 index 0000000..37a6f5a --- /dev/null +++ b/libloragw/tst/test_loragw_reg.c @@ -0,0 +1,134 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Minimum test program for the loragw_spi 'library' + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include <stdint.h> +#include <stdio.h> + +#include "loragw_reg.h" + +/* -------------------------------------------------------------------------- */ +/* --- MAIN FUNCTION -------------------------------------------------------- */ + +#define BURST_TEST_LENGTH 8192 + +int main() +{ + int32_t read_value, test_value; + uint16_t lfsr; + uint8_t burst_buffout[BURST_TEST_LENGTH]; + uint8_t burst_buffin[BURST_TEST_LENGTH]; + int i; + + printf("Beginning of test for loragw_reg.c\n"); + + lgw_connect(false, 129E3); + /* 2 SPI transactions: + -> 0x80 0x00 <- 0x00 0x00 forcing page 0 + -> 0x01 0x00 <- 0x00 0x64 checking version + */ + + /* --- READ TEST --- */ + + lgw_reg_w(LGW_SOFT_RESET, 1); + lgw_reg_check(stdout); + + /* --- READ/WRITE COHERENCY TEST --- */ + + /* 8b unsigned */ + test_value = 197; /* 11000101b */ + lgw_reg_w(LGW_IMPLICIT_PAYLOAD_LENGHT, test_value); + lgw_reg_r(LGW_IMPLICIT_PAYLOAD_LENGHT, &read_value); + printf("IMPLICIT_PAYLOAD_LENGHT = %d (should be %d)\n", read_value, test_value); + + /* 8b signed */ + /* NO SUCH REG AVAILABLE */ + // /* RADIO_SELECT is normally unsigned, modify it manually in loragw_reg.c */ + // test_value = -59; /* 11000101b */ + // lgw_reg_w(LGW_RADIO_SELECT, test_value); + // lgw_reg_r(LGW_RADIO_SELECT, &read_value); + // printf("RADIO_SELECT = %d (should be %d)\n", read_value, test_value); + + /* less than 8b, with offset, unsigned */ + test_value = 11; /* 1011b */ + lgw_reg_w(LGW_FRAME_SYNCH_PEAK2_POS, test_value); + lgw_reg_r(LGW_FRAME_SYNCH_PEAK2_POS, &read_value); + printf("FRAME_SYNCH_PEAK2_POS = %d (should be %d)\n", read_value, test_value); + + /* less than 8b, with offset, signed */ + /* NO SUCH REG AVAILABLE */ + // /* MBWSSF_FRAME_SYNCH_PEAK2_POS is normally unsigned, modify it manually in loragw_reg.c */ + // test_value = -5; /* 1011b */ + // lgw_reg_w(LGW_MBWSSF_FRAME_SYNCH_PEAK2_POS, test_value); + // lgw_reg_r(LGW_MBWSSF_FRAME_SYNCH_PEAK2_POS, &read_value); + // printf("MBWSSF_FRAME_SYNCH_PEAK2_POS = %d (should be %d)\n", read_value, test_value); + + /* 16b unsigned */ + test_value = 49253; /* 11000000 01100101b */ + lgw_reg_w(LGW_PREAMBLE_SYMB1_NB, test_value); + lgw_reg_r(LGW_PREAMBLE_SYMB1_NB, &read_value); + printf("PREAMBLE_SYMB1_NB = %d (should be %d)\n", read_value, test_value); + + /* 16b signed */ + /* NO SUCH REG AVAILABLE */ + // /* CAPTURE_PERIOD is normally unsigned, modify it manually in loragw_reg.c */ + // test_value = -16283; /* 11000000 01100101b */ + // lgw_reg_w(LGW_CAPTURE_PERIOD, test_value); + // lgw_reg_r(LGW_CAPTURE_PERIOD, &read_value); + // printf("CAPTURE_PERIOD = %d (should be %d)\n", read_value, test_value); + + /* between 8b and 16b, unsigned */ + test_value = 3173; /* 1100 01100101b */ + lgw_reg_w(LGW_ADJUST_MODEM_START_OFFSET_SF12_RDX4, test_value); + lgw_reg_r(LGW_ADJUST_MODEM_START_OFFSET_SF12_RDX4, &read_value); + printf("ADJUST_MODEM_START_OFFSET_SF12_RDX4 = %d (should be %d)\n", read_value, test_value); + + /* between 8b and 16b, signed */ + test_value = -1947; /* 11000 01100101b */ + lgw_reg_w(LGW_IF_FREQ_1, test_value); + lgw_reg_r(LGW_IF_FREQ_1, &read_value); + printf("IF_FREQ_1 = %d (should be %d)\n", read_value, test_value); + + /* --- BURST WRITE AND READ TEST --- */ + + /* initialize data for SPI test */ + lfsr = 0xFFFF; + for(i=0; i<BURST_TEST_LENGTH; ++i) { + burst_buffout[i] = (uint8_t)(lfsr ^ (lfsr >> 4)); + /* printf("%05d # 0x%04x 0x%02x\n", i, lfsr, burst_buffout[i]); */ + lfsr = (lfsr & 1) ? ((lfsr >> 1) ^ 0x8679) : (lfsr >> 1); + } + + lgw_reg_wb(LGW_TX_DATA_BUF_DATA, burst_buffout, 256); + lgw_reg_rb(LGW_RX_DATA_BUF_DATA, burst_buffin, 256); + + /* impossible to check in software, + RX_DATA_BUF_DATA is read-only, + TX_DATA_BUF_DATA is write only, + use a logic analyser */ + + /* --- END OF TEST --- */ + + lgw_disconnect(); + /* no SPI transaction */ + + printf("End of test for loragw_reg.c\n"); + return 0; +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/tst/test_loragw_spi.c b/libloragw/tst/test_loragw_spi.c new file mode 100644 index 0000000..872a075 --- /dev/null +++ b/libloragw/tst/test_loragw_spi.c @@ -0,0 +1,85 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Minimum test program for the loragw_spi 'library' + Use logic analyser to check the results. + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include <stdint.h> +#include <stdio.h> + +#include "loragw_spi.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +#define BURST_TEST_SIZE 2500 /* >> LGW_BURST_CHUNK */ +#define TIMING_REPEAT 1 /* repeat transactions multiple times for timing characterisation */ + +/* -------------------------------------------------------------------------- */ +/* --- MAIN FUNCTION -------------------------------------------------------- */ + +int main() +{ + int i; + void *spi_target = NULL; + uint8_t data = 0; + uint8_t dataout[BURST_TEST_SIZE]; + uint8_t datain[BURST_TEST_SIZE]; + uint8_t spi_mux_mode = LGW_SPI_MUX_MODE0; + + for (i = 0; i < BURST_TEST_SIZE; ++i) { + dataout[i] = 0x30 + (i % 10); /* ASCCI code for 0 -> 9 */ + datain[i] = 0x23; /* garbage data, to be overwritten by received data */ + } + + printf("Beginning of test for loragw_spi.c\n"); + lgw_spi_open(&spi_target); + + /* normal R/W test */ + for (i = 0; i < TIMING_REPEAT; ++i) + lgw_spi_w(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0xAA, 0x96); + for (i = 0; i < TIMING_REPEAT; ++i) + lgw_spi_r(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0x55, &data); + + /* burst R/W test, small bursts << LGW_BURST_CHUNK */ + for (i = 0; i < TIMING_REPEAT; ++i) + lgw_spi_wb(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0x55, dataout, 16); + for (i = 0; i < TIMING_REPEAT; ++i) + lgw_spi_rb(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0x55, datain, 16); + + /* burst R/W test, large bursts >> LGW_BURST_CHUNK */ + for (i = 0; i < TIMING_REPEAT; ++i) + lgw_spi_wb(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0x5A, dataout, ARRAY_SIZE(dataout)); + for (i = 0; i < TIMING_REPEAT; ++i) + lgw_spi_rb(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0x5A, datain, ARRAY_SIZE(datain)); + + /* last read (blocking), just to be sure no to quit before the FTDI buffer is flushed */ + lgw_spi_r(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0x55, &data); + printf("data received (simple read): %d\n",data); + + lgw_spi_close(spi_target); + printf("End of test for loragw_spi.c\n"); + + return 0; +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..cdfd8d1 --- /dev/null +++ b/readme.md @@ -0,0 +1,410 @@ + / _____) _ | | + ( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | + (______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +LoRa Gateway project +===================== + +1. Core library: libloragw +--------------------------- + +This directory contains the sources of the library to build a gateway based on +a Semtech LoRa multi-channel RF receiver (a.k.a. concentrator). +Once compiled all the code is contained in the libloragw.a file that will be +statically linked (ie. integrated in the final executable). + +The library also comes with a bunch of basic tests programs that are used to +test the different sub-modules of the library. + +2. Helper programs +------------------- + +Those programs are included in the project to provide examples on how to use +the HAL library, and to help the system builder test different parts of it. + +### 2.1. util_pkt_logger ### + +This software is used to set up a LoRa concentrator using a JSON configuration +file and then record all the packets received in a log file, indefinitely, until +the user stops the application. + +### 2.2. util_spi_stress ### + +This software is used to check the reliability of the link between the host +platform (on which the program is run) and the LoRa concentrator register file +that is the interface through which all interaction with the LoRa concentrator +happens. + +### 2.3. util_tx_test ### + +This software is used to send test packets with a LoRa concentrator. The packets +contain little information, on no protocol (ie. MAC address) information but +can be used to assess the functionality of a gateway downlink using other +gateways as receivers. + +### 2.4. util_tx_continuous ### + +This software is used to set LoRa concentrator in Tx continuous mode, +for spectral measurement. + +### 2.5. util_spectral_scan ### + +This software is used to scan the spectral band in background, where the LoRa +gateway operates. + +### 2.6. util_lbt_test ### + +This software is used to test "Listen-Before-Talk" channels timestamps. + +3. Helper scripts +----------------- + +### 3.1. reset_lgw.sh + +This script must be launched on IoT Start Kit platform to reset concentrator +chip through GPIO, before starting any application using the concentrator. + +4. Changelog +------------- + +### v5.0.1 ### + +* HAL: Reworked the way the TX start delay is calculated, taking into account +the delay introduced by TX notch filter (when enabled) and the delay linked to +signal bandwidth separately. + +### v5.0.0 ### + +* HAL: Changed GPS module to get native GPS time (monotonic, no leap second). +**WARNING**: The native GPS time is not given in standard NMEA messages, so we +get it from a proprietary message of the GPS module used on gateway reference +design, u-blox 7. If you are not using the same GPS module, you may have to +update the lgw_parse_ubx() function. +* HAL: Added lgw_cnt2gps() and lgw_gps2cnt() functions for SX1301<->GPS time +conversion. +* HAL: Changed serial port configuration for GPS to properly handle binary +messages (UBX). +* HAL: Added a lgw_gps_disable() function to restore serial configuration as +it was before HAL initialization. +* HAL: Fixed packet time on air calculation using the actual packet preamble +size. +* HAL: Adjusted TX_START_DELAY based on the board reference design to ensure +that a packet is sent exactly at 1500µs after the TX trigger (TIMESTAMP or PPS), +with a tolerance of +/- 1µs. This is mandatory to comply with LoRaWAN +specification for Class-B beaconing precise timing. +**WARNING**: This release provides tx start delay values to be used for Semtech +reference designs AP1 and AP2. The HAL automatically detects the board version +by detecting a FPGA or not. If you are using a different reference design or +a different FPGA binary version than the one provided with this release, the +value to be used for TX start delay may be different. + +### v4.1.3 ### + +* HAL: Reference clock frequency error improvement: The lora_gateway HAL has +been updated (3 registers changed) to improve the performance of all gateways +based on SX130x. The fix greatly improves the reception of packet at SF12, when +the frequency offset of the incoming packet is large (mostly below -20ppm of +frequency offset). + +WARNING: Systems which do not have the patch will be more prone to packet loss +over time, when the crystals of the end-devices will be ageing and have more +frequency offset. + +### v4.1.2 ### + +* HAL: Changed configuration of IQ polarity of FPGA for TX to comply with FPGA +version greater than v27. (Only required for AP2 Semtech reference design) +* HAL: Updated default LoRa preamble size according to LoRaWAN spec. + +### v4.1.1 ### + +* HAL: Fixed bug in "Listen-Before-Talk" which was preventing from configuring +the Scan Time to 5ms. +* MISC: Added GPIO number to reset_lgw.sh command arguments. + +### v4.1.0 ### + +* HAL: Reworked "Listen-Before-Talk" feature to have more flexibility to define +LBT channels frequency, and to be able to have spectral scan running in parallel +* HAL: Updated lgw_time_on_air() function for FSK packets +* HAL: Disabled GPS UART input being re-echoed as output to avoid sending wrong +commands to GPS module +* HAL: Fixed IF frequency configuration check issue for channel bandwidths 250K +and 500HKz. +* FPGA: Updated to v31 for new LBT and spectral scan design. +* util_spectral_scan: updated to match new spectral scan FPGA sequence +* util_lbt_test: updated to match LBT rework + +Note: The provided LBT feature has been validated for Japan only, and supports +8 downlink channels maximum. + +### v4.0.1 ### + +* HAL: SX1301AP2: Only FPGA v27 is supported, removed (v18,v19) from the list + of supported FPGA images. + +WARNING: If you are using a Semtech SX1301AP2 ref design (GW1.5), the FPGA must +be reprogrammed with one of the images provided with this release (fpga/ dir). + +### v4.0.0 ### + +* HAL: Added "Listen-Before-Talk" support for Semtech SX1301AP2 Ref Design. + A description of the feature implementation can be found in + libloragw/readme.md. +* HAL: Updated FSK RSSI calculation for better linearization +* util_lbt_test: New utility provided for basic "Listen-Before-Talk" testing. +* util_tx_test: Extended to configure and test "LBT" through the HAL. +* Added a reset_lgw.sh script to be used with IoT Starter Kit (v1.0) to reset +the concentrator through the HOST GPIO pin. + +### v3.2.1 ### + +* HAL: Fixed downlink support for SX1301AP2 reference design: soft reset of the +FPGA was missing for proper IQ inversion configuration. +* HAL: Added support for several versions of FPGA (currently v18 and v19) +* HAL: Reduced radio TX PLL bandwidth to reduce the noise level. +* util_tx_test: Added FSK support and added minimal TX gain LUT. +* util_spectral_scan: Removed FPGA soft reset, now done by the HAL. +* util_tx_continous: reworked to use HAL functions instead of 'manual' config, +and use same SX1301 calibration firmware as the HAL. +* Updated all makefiles to handle the creation of obj directory when necessary. +* Change cs_change usage policy in SPI module to let the driver handle the chip +select. + +### v3.2.0 ### + +* Added support for SX1301AP2 reference design (with FPGA and additional +SX127x). When a FPGA is detected at startup, the HAL automatically adapts SPI +communication requests (using SPI header or not). +* Added util_spectral_scan diagnostic tool to scan the spectral band in +background, where the LoRa gateway operates. (can only be used with SX1301AP2 +or similar design). By default it uses the same SPI device as the one used by +the HAL, but it can be changed depending on the hardware architecture on which +it is used by updating the SPI_DEV_PATH constant defined in file +util_spectral_scan/src/loragw_fpga_spi.c. +Note: when using same SPI device from 2 applications, we rely on the host SPI +driver and OS to properly handle concurrent SPI requests. It has been tested on +Raspberry Pi / Raspbian with spi_bcm2708 driver.* Removed SPI FTDI support due +to lack of performances to properly handle heavy packet traffic. Only native +SPI usage is recommended. +* HAL: added a check that SX1301 firmwares have been properly loaded at startup. + +### v3.1.0 ### + +* Removed GPIO module from HAL, that was specific to IoT Starter Kit platform. +GPIO configuration will be done from application script instead. +* Removed CFG_BRD configuration from library.cfg, not needed anymore + +### v3.0.2 ### + +* Bugfix: Fixed frequency calculation on uplinks: lgw_receive() function was +using a variable to calculate the frequency before it was initialized with +correct value. +* Bugfix: util_pkt_logger crashed when no gateway_ID is not defined in +global_conf.json + +### v3.0.1 ### + +* Bufgix: Fixed util_tx_continuous compilation issue, by adding empty obj +directory +* Bugfix: Fixed HAL compilation issue for CFG_SPI=ftdi, removed dependency on +loragw_gpio in this case + +### v3.0.0 ### + +* Added new HAL function lgw_board_setconf() to configure board/concentrator +specific parameters: network type (LoRa public or private), concentrator clock +source. Note: those parameters are not any more set from the library.cfg file +configuration (CFG_NET, CFG_BRD), +and should be passed at initialization by the application. +* Added new HAL function lgw_txgain_setconf() to configure concentrator TX gain +table. It can now be dynamically set by the application at initialization time. +* Changed HAL function lgw_rxrf_setconf(), it will now also configure the radio +type (CFG_RADIO has been removed from library.cfg), the RSSI offset to be used +for this radio and if TX is enabled or not on this radio. +* Added support of IoT Starter Kit platform, which is now the default board. +* Added util_tx_continuous utility for gateway TX power calibration and +spectral emission measurements/qualification. +* Removed CFG_BAND configuration from library.cfg. Band configuration is done +by application and passed dynamically at initialization time. +* Updated makefiles to allow cross compilation from environment variable (ARCH, +CROSS_COMPILE). + +** WARNING: ** +** Known issue: a problem with carrier leakage calibration has been seen on +433MHz boards. ** + +### v2.0.0 ### + +* Added support for Kerlink 868 27dBm gateway +* Updated global_conf.eu868.json (in packet logger) to new LoRaWAN frequency +plan +* Added version numbers to AGC, arbiter and calibration firmware (those +versions are checked at startup) +* Added test_loragw_cal to test radio calibrations +* Fixed minor bug in error coverage in register read/write functions + +/!\ warning: Kerlink 868 27dBm gateway includes a FPGA that MUST be programmed +before running any application + +### v1.7.0 ### + +* Added TX 'start delay' compensation for timestamp mode (fix time window +alignment issue at low SF and/or high BW) +* Added adaptive narrowband/wideband TX filtering for LoRa +* Added a command-line option to set CR in util_tx_test +* Added notes for TX 'start delay' in immediate and triggered mode + +/!\ warning: due to start delay compensation being implemented, TX that were +previously 1.5ms late will be sent on time. At low datarate, this is not an +issue. At high LoRa data rate (and FSK) you might have to adjust your timing. + +### v1.6.0 ### + +* Fixed bug with 250kHz and 500 kHz TX filtering +* Adjusted FSK timestamp calibration in RX for accurate RX/TX alignment +* Added lgw_abort_tx() function to stop a TX at any time (scheduled or ongoing) +* Added support for user-settable FSK sync word (same for RX and TX) +* Added support for the Chinese 780 MHz band +* Added support for Kerlink 433 gateway +* Added support for Cisco 433, 470 & 780 MHz concentrators boards + +### v1.5.0 ### + +* Adding option to isolate public LoRa MAC networks at PHY level. + +### v1.4.1 ### + +* Enabling support for FSK per LoRa MAC specification +* Adjusting TX and RX calibration set on 868 reference board +* Added specific RX/TX calibration set for Kerlink 868 "IoT station" gateway +* Changed default SPI port for native driver to Kerlink SPI device number + +### v1.4.0 ### + +* Added calibration routine to optimize RF performance +* Added support for SX1301 433 MHz reference board +* Improved AGC firmware +* Improved RSSI accuracy +* Improved utilities Makefile + +### v1.3.0 ### + +* Added TX power management. +* Added full support for SX1301 reference board. +* Changed build system with configuration for multiple chip/radio/band support. +* SX125x bandwidth set to 1MHz by default (was 800 kHz). +* Solved warnings with 64b integer printf when compiling on x86_64. +* Renamed helper programs to reduce the concentrator vs. gateway confusion. + +### v1.2.2 ### + +* Added a GPIO toggle on the FTDI SPI module to reset the SX1301 board. + +### v1.2.1 ### + +* Fixed 'floating point exception' crash when concentrator returned a packet +with SF=0 (CRC error on LoRa header). +* Fixed buggy timezone handling. + +### v1.2.0 ### + +* Added feature: new GPS module in the library for synchronization. +* Removed feature: no more missed deadline detection in TX because of +incompatibility with GPS. +* Added documentation for GPS and legal notice. +* Added flags in Makefiles for easier cross-compilation. + +### v1.1.0 ### + +* Fixed bug 'no TX on radio B' (rfch 1). +* Added feature: concentrator processing delay compensation in the receive() +function for accurate 'end of packet' even timestamping. +* Added feature: TX 'start delay' compensation in the send() function to emit +packet exactly on target timestamp. +* Added feature: timestamp counter verification in send() function, return an +error if scheduling was too late. +* Switched license to 'Revised BSD'. + +### v1.0.0 (from beta 8) ### + +* Switched FTDI as default SPI phy layer in library.cfg. +* Fixed a bug in TX power control; still only two TW power available, 14 and +24dBm. +* Changed library directory name from loragw_hal to libloragw to follow usual +conventions. + +### Beta 8 (from beta 7) ### + +* API: lgw_receive now return info on RX frequency and RF path for each packet +(no need to keep track of RF/IF settings). +* Unified some portion of the code with the 470 MHz variant of the HAL (use +SX1255 radios instead of SX1257). +* Improved AGC and ARB firmwares. +* Adding -Wall -Wextra for compilation, fixing all the new warnings for cleaner +code. +* Fixed bugs in handling of FSK datarate. +* test_loragw_hal now dumps the content of all LoRa registers after +configuration in reg_dump.log. + +### Beta 7 (from beta 5) ### + +* Reduced number of SPI transactions to fetch a packet (improved number a +packets par second that can be downloaded from concentrator). +* Streamlined build process, main target is now a static library: libloragw.a. +* Change memory allocation for payload: they are now part of the struct for +TX/RX, no need to malloc/free. +* All RX chains can use any of the two radios now. +* FSK is available and working in TX and RX (variable length mode). +* Calibrated RSSI for FSK. +* lgw_connect now check the CHIP_ID. +* Added a license file and a changelog. +* Added a function returning a version string to allow identification of the +version/options once compiled. + +### Beta 6 ### + +Private release, not taken into account in that changelog. + +### Beta 5 (from beta 4) ### + +* Updated registers, firmware and configuration to align with r986 bitstream +revision. +* Calibrated RSSI for LoRa "multi" and LoRa "stand alone" modems. +* Renamed some confusing TX status code. +* Added preliminary FSK support. + +### Beta 4 (from beta 3) ### + +* Unified build environment with selectable SPI layer (Linux native or FTDI +SPI-over-USB bridge). +* Remove the 500 kHz limit on radio bandwith, back to the nominal 800 kHz. +* Renamed debug flags. + +5. Legal notice +---------------- + +The information presented in this project documentation does not form part of +any quotation or contract, is believed to be accurate and reliable and may be +changed without notice. No liability will be accepted by the publisher for any +consequence of its use. Publication thereof does not convey nor imply any +license under patent or other industrial or intellectual property rights. +Semtech assumes no responsibility or liability whatsoever for any failure or +unexpected operation resulting from misuse, neglect improper installation, +repair or improper handling or unusual physical or electrical stress +including, but not limited to, exposure to parameters beyond the specified +maximum ratings or operation outside the specified range. + +SEMTECH PRODUCTS ARE NOT DESIGNED, INTENDED, AUTHORIZED OR WARRANTED TO BE +SUITABLE FOR USE IN LIFE-SUPPORT APPLICATIONS, DEVICES OR SYSTEMS OR OTHER +CRITICAL APPLICATIONS. INCLUSION OF SEMTECH PRODUCTS IN SUCH APPLICATIONS IS +UNDERSTOOD TO BE UNDERTAKEN SOLELY AT THE CUSTOMER'S OWN RISK. Should a +customer purchase or use Semtech products for any such unauthorized +application, the customer shall indemnify and hold Semtech and its officers, +employees, subsidiaries, affiliates, and distributors harmless against all +claims, costs damages and attorney fees which could arise. + +*EOF* diff --git a/reset_lgw.sh b/reset_lgw.sh new file mode 100755 index 0000000..77de63e --- /dev/null +++ b/reset_lgw.sh @@ -0,0 +1,62 @@ +#!/bin/sh + +# This script is intended to be used on IoT Starter Kit platform, it performs +# the following actions: +# - export/unpexort GPIO7 used to reset the SX1301 chip +# +# Usage examples: +# ./reset_lgw.sh stop +# ./reset_lgw.sh start + +# The reset pin of SX1301 is wired with RPi GPIO7 +# If used on another platform, the GPIO number can be given as parameter. +if [ -z "$2" ]; then + IOT_SK_SX1301_RESET_PIN=7 +else + IOT_SK_SX1301_RESET_PIN=$2 +fi + +echo "Accessing concentrator reset pin through GPIO$IOT_SK_SX1301_RESET_PIN..." + +WAIT_GPIO() { + sleep 0.1 +} + +iot_sk_init() { + # setup GPIO 7 + echo "$IOT_SK_SX1301_RESET_PIN" > /sys/class/gpio/export; WAIT_GPIO + + # set GPIO 7 as output + echo "out" > /sys/class/gpio/gpio$IOT_SK_SX1301_RESET_PIN/direction; WAIT_GPIO + + # write output for SX1301 reset + echo "1" > /sys/class/gpio/gpio$IOT_SK_SX1301_RESET_PIN/value; WAIT_GPIO + echo "0" > /sys/class/gpio/gpio$IOT_SK_SX1301_RESET_PIN/value; WAIT_GPIO + + # set GPIO 7 as input + echo "in" > /sys/class/gpio/gpio$IOT_SK_SX1301_RESET_PIN/direction; WAIT_GPIO +} + +iot_sk_term() { + # cleanup GPIO 7 + if [ -d /sys/class/gpio/gpio$IOT_SK_SX1301_RESET_PIN ] + then + echo "$IOT_SK_SX1301_RESET_PIN" > /sys/class/gpio/unexport; WAIT_GPIO + fi +} + +case "$1" in + start) + iot_sk_term + iot_sk_init + ;; + stop) + iot_sk_term + ;; + *) + echo "Usage: $0 {start|stop} [<gpio number>]" + exit 1 + ;; +esac + +exit 0 diff --git a/util_lbt_test/Makefile b/util_lbt_test/Makefile new file mode 100644 index 0000000..de937e4 --- /dev/null +++ b/util_lbt_test/Makefile @@ -0,0 +1,65 @@ +### Application-specific constants + +APP_NAME := util_lbt_test + +### Environment constants + +LGW_PATH ?= ../libloragw +ARCH ?= +CROSS_COMPILE ?= + +### External constant definitions + +include $(LGW_PATH)/library.cfg + +### Constant symbols + +CC := $(CROSS_COMPILE)gcc +AR := $(CROSS_COMPILE)ar + +CFLAGS=-O2 -Wall -Wextra -std=c99 -Iinc -I. + +OBJDIR = obj + +### Constants for LoRa concentrator HAL library +# List the library sub-modules that are used by the application + +LGW_INC = $(LGW_PATH)/inc/config.h +LGW_INC += $(LGW_PATH)/inc/loragw_reg.h +LGW_INC += $(LGW_PATH)/inc/loragw_fpga.h + +### Linking options + +LIBS := -lloragw -lrt -lm + +### General build targets + +all: $(APP_NAME) + +clean: + rm -f $(OBJDIR)/*.o + rm -f $(APP_NAME) + +### HAL library (do no force multiple library rebuild even with 'make -B') + +$(LGW_PATH)/inc/config.h: + @if test ! -f $@; then \ + $(MAKE) all -C $(LGW_PATH); \ + fi + +$(LGW_PATH)/libloragw.a: $(LGW_INC) + @if test ! -f $@; then \ + $(MAKE) all -C $(LGW_PATH); \ + fi + +### Main program compilation and assembly +$(OBJDIR): + mkdir -p $(OBJDIR) + +$(OBJDIR)/$(APP_NAME).o: src/$(APP_NAME).c $(LGW_INC) | $(OBJDIR) + $(CC) -c $(CFLAGS) -I$(LGW_PATH)/inc $< -o $@ + +$(APP_NAME): $(OBJDIR)/$(APP_NAME).o $(LGW_PATH)/libloragw.a + $(CC) -L$(LGW_PATH) $< -o $@ $(LIBS) + +### EOF diff --git a/util_lbt_test/readme.md b/util_lbt_test/readme.md new file mode 100644 index 0000000..db6cd58 --- /dev/null +++ b/util_lbt_test/readme.md @@ -0,0 +1,50 @@ + / _____) _ | | + ( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | + (______/|_____)_|_|_| \__)_____)\____)_| |_| + ©2013 Semtech-Cycleo + +Lora Gateway LBT basic test application +======================================= + +1. Introduction +---------------- + +This software configures the FPGA for "Liste-Before-Talk" feature and +continuously reads the LBT channels timestamps which indicate when was the last +instant when the channel was free. + +2. Dependencies +---------------- + +A SX1301AP2 Ref Design board with its FPGA programmed with LBT feature + +3. Usage +--------- + +Before running the util_lbt_test application, the concentrator MUST be first +started with the HAL, using for example util_pkt_logger or the packet forwarder +with LBT feature disabled, as the util_lbt_test will configure it. + +For a description of the command line options available: +./util_lbt_test -h + +ex: +./util_lbt_test -f 867.1 -r -80 -s 5000 + +This will set 8 LBT channels, starting from 867.1 MHz, then each subsequent +channel being set to the frequency of the previous channel +200 KHz (867.3, +867.5, ...). + +The above test will run for ever, with a CHANNEL_SCAN_TIME of 5000µs +and a target RSSI of -80dBm. + +Please refer to the lora_gateway library readme.md to get more details on the +LBT feature implementation and configuration. + +4. Changelog +------------- + +2016-03-03 v1.0 Initial version +2016-08-31 v1.1 LBT feature rework diff --git a/util_lbt_test/src/util_lbt_test.c b/util_lbt_test/src/util_lbt_test.c new file mode 100644 index 0000000..1c8b185 --- /dev/null +++ b/util_lbt_test/src/util_lbt_test.c @@ -0,0 +1,274 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Listen Before Talk basic test application + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Michael Coracin +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +/* fix an issue between POSIX and C99 */ +#if __STDC_VERSION__ >= 199901L + #define _XOPEN_SOURCE 600 +#else + #define _XOPEN_SOURCE 500 +#endif + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ +#include <stdio.h> /* printf fprintf sprintf fopen fputs */ + +#include <signal.h> /* sigaction */ +#include <unistd.h> /* getopt access */ +#include <stdlib.h> /* rand */ + +#include "loragw_aux.h" +#include "loragw_reg.h" +#include "loragw_hal.h" +#include "loragw_radio.h" +#include "loragw_fpga.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS & CONSTANTS ------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#define MSG(args...) fprintf(stderr, args) /* message that is destined to the user */ + +#define DEFAULT_SX127X_RSSI_OFFSET -1 + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE VARIABLES (GLOBAL) ------------------------------------------- */ + +/* signal handling variables */ +struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */ +static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */ +static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ + +static void sig_handler(int sigio); + +void usage (void); + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ + +static void sig_handler(int sigio) { + if (sigio == SIGQUIT) { + quit_sig = 1;; + } else if ((sigio == SIGINT) || (sigio == SIGTERM)) { + exit_sig = 1; + } +} + +/* describe command line options */ +void usage(void) { + printf("Available options:\n"); + printf(" -h print this help\n"); + printf(" -f <float> frequency in MHz of the first LBT channel\n"); + printf(" -o <int> offset in dB to be applied to the SX127x RSSI [-128..127]\n"); + printf(" -r <int> target RSSI: signal strength target used to detect if the channel is clear or not [-128..0]\n"); + printf(" -s <uint> scan time in µs for all 8 LBT channels [128,5000]\n"); +} + +/* -------------------------------------------------------------------------- */ +/* --- MAIN FUNCTION -------------------------------------------------------- */ + +int main(int argc, char **argv) +{ + int i; + int xi = 0; + + /* in/out variables */ + double f1 = 0.0; + uint32_t f_init = 0; /* in Hz */ + uint32_t f_start = 0; /* in Hz */ + uint16_t loop_cnt = 0; + int8_t rssi_target_dBm = -80; + uint16_t scan_time_us = 128; + uint32_t timestamp; + uint8_t rssi_value; + int8_t rssi_offset = DEFAULT_SX127X_RSSI_OFFSET; + int32_t val, val2; + int channel; + uint32_t freq_offset; + + /* parse command line options */ + while ((i = getopt (argc, argv, "h:f:s:r:o:")) != -1) { + switch (i) { + case 'h': + usage(); + return EXIT_FAILURE; + break; + + case 'f': + i = sscanf(optarg, "%lf", &f1); + if ((i != 1) || (f1 < 30.0) || (f1 > 3000.0)) { + MSG("ERROR: Invalid LBT start frequency\n"); + usage(); + return EXIT_FAILURE; + } else { + f_start = (uint32_t)((f1*1e6) + 0.5);/* .5 Hz offset to get rounding instead of truncating */ + } + break; + case 's': + i = sscanf(optarg, "%i", &xi); + if ((i != 1) || ((xi != 128) && (xi != 5000))) { + MSG("ERROR: scan_time_us must be 128 or 5000 \n"); + usage(); + return EXIT_FAILURE; + } else { + scan_time_us = xi; + } + break; + case 'r': + i = sscanf(optarg, "%i", &xi); + if ((i != 1) || ((xi < -128) && (xi > 0))) { + MSG("ERROR: rssi_target must be b/w -128 & 0 \n"); + usage(); + return EXIT_FAILURE; + } else { + rssi_target_dBm = xi; + } + break; + case 'o': /* -o <int> SX127x RSSI offset [-128..127] */ + i = sscanf(optarg, "%i", &xi); + if((i != 1) || (xi < -128) || (xi > 127)) { + MSG("ERROR: rssi_offset must be b/w -128 & 127\n"); + usage(); + return EXIT_FAILURE; + } else { + rssi_offset = (int8_t)xi; + } + break; + default: + MSG("ERROR: argument parsing use -h option for help\n"); + usage(); + return EXIT_FAILURE; + } + } + + MSG("INFO: Starting LoRa Gateway v1.5 LBT test\n"); + + /* configure signal handling */ + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_handler = sig_handler; + sigaction(SIGQUIT, &sigact, NULL); + sigaction(SIGINT, &sigact, NULL); + sigaction(SIGTERM, &sigact, NULL); + + /* Connect to concentrator */ + i = lgw_connect(false, LGW_DEFAULT_NOTCH_FREQ); + if (i != LGW_REG_SUCCESS) { + MSG("ERROR: lgw_connect() did not return SUCCESS\n"); + return EXIT_FAILURE; + } + + /* Check if FPGA supports LBT */ + lgw_fpga_reg_r(LGW_FPGA_FEATURE, &val); + if (TAKE_N_BITS_FROM((uint8_t)val, 2, 1) != true) { + MSG("ERROR: LBT is not supported (0x%x)\n", (uint8_t)val); + return EXIT_FAILURE; + } + + /* Get FPGA lowest frequency for LBT channels */ + lgw_fpga_reg_r(LGW_FPGA_LBT_INITIAL_FREQ, &val); + switch (val) { + case 0: + f_init = 915000000; + break; + case 1: + f_init = 863000000; + break; + default: + MSG("ERROR: LBT start frequency %d is not supported\n", val); + return EXIT_FAILURE; + } + + /* Initialize 1st LBT channel freq if not given by user */ + if (f_start == 0) { + f_start = f_init; + } else if (f_start < f_init) { + MSG("ERROR: LBT start frequency %u is not supported (f_init=%u)\n", f_start, f_init); + return EXIT_FAILURE; + } + MSG("FREQ: %u\n", f_start); + + /* Configure SX127x and read few RSSI points */ + lgw_setup_sx127x(f_init, MOD_FSK, LGW_SX127X_RXBW_100K_HZ, rssi_offset); /* 200KHz LBT channels */ + for (i = 0; i < 100; i++) { + lgw_sx127x_reg_r(0x11, &rssi_value); /* 0x11: RegRssiValue */ + MSG("SX127x RSSI:%i dBm\n", -(rssi_value/2)); + wait_ms(10); + } + + /* Configure LBT */ + val = -2*(rssi_target_dBm); + lgw_fpga_reg_w(LGW_FPGA_RSSI_TARGET, val); + for (i = 0; i < LBT_CHANNEL_FREQ_NB; i++) { + freq_offset = (f_start - f_init)/100E3 + i*2; /* 200KHz between each channel */ + lgw_fpga_reg_w(LGW_FPGA_LBT_CH0_FREQ_OFFSET+i, (int32_t)freq_offset); + if (scan_time_us == 5000) { /* configured to 128 by default */ + lgw_fpga_reg_w(LGW_FPGA_LBT_SCAN_TIME_CH0+i, 1); + } + } + + lgw_fpga_reg_r(LGW_FPGA_RSSI_TARGET, &val); + MSG("RSSI_TARGET = %d\n", val); + if (val != (-2*rssi_target_dBm)) { + MSG("ERROR: failed to read back RSSI target register value\n"); + return EXIT_FAILURE; + } + for (i = 0; i < LBT_CHANNEL_FREQ_NB; i++) { + lgw_fpga_reg_r(LGW_FPGA_LBT_CH0_FREQ_OFFSET+i, &val); + lgw_fpga_reg_r(LGW_FPGA_LBT_SCAN_TIME_CH0+i, &val2); + MSG("CH_%i: freq=%u (offset=%i), scan_time=%u (%i)\n", i, (uint32_t)((val*100E3)+f_init), val, (val2==1)?5000:128, val2); + } + lgw_fpga_reg_r(LGW_FPGA_VERSION, &val); + MSG("FPGA VERSION = %d\n", val); + + /* Enable LBT FSM */ + lgw_fpga_reg_w(LGW_FPGA_CTRL_FEATURE_START, 1); + + /* Start test */ + while ((quit_sig != 1) && (exit_sig != 1)) { + MSG("~~~~\n"); + for (channel = 0; channel < LBT_CHANNEL_FREQ_NB; channel++) { + /* Select LBT channel */ + lgw_fpga_reg_w(LGW_FPGA_LBT_TIMESTAMP_SELECT_CH, channel); + + /* Get last instant when the selected channel was free */ + lgw_fpga_reg_r(LGW_FPGA_LBT_TIMESTAMP_CH, &val); + timestamp = (uint32_t)(val & 0x0000FFFF) * 256; /* 16bits (1LSB = 256µs) */ + MSG(" TIMESTAMP_CH%u = %u\n", channel, timestamp); + } + + loop_cnt += 1; + wait_ms(400); + } + + /* close SPI link */ + i = lgw_disconnect(); + if (i != LGW_REG_SUCCESS) { + MSG("ERROR: lgw_disconnect() did not return SUCCESS\n"); + return EXIT_FAILURE; + } + + MSG("INFO: Exiting LoRa Gateway v1.5 LBT test successfully\n"); + return EXIT_SUCCESS; +} + +/* --- EOF ------------------------------------------------------------------ */ + diff --git a/util_pkt_logger/Makefile b/util_pkt_logger/Makefile new file mode 100644 index 0000000..264ccc1 --- /dev/null +++ b/util_pkt_logger/Makefile @@ -0,0 +1,70 @@ +### Application-specific constants + +APP_NAME := util_pkt_logger + +### Environment constants + +LGW_PATH ?= ../libloragw +ARCH ?= +CROSS_COMPILE ?= + +### External constant definitions + +include $(LGW_PATH)/library.cfg + +### Constant symbols + +CC := $(CROSS_COMPILE)gcc +AR := $(CROSS_COMPILE)ar + +CFLAGS=-O2 -Wall -Wextra -std=c99 -Iinc -I. + +OBJDIR = obj + +### Constants for LoRa concentrator HAL library +# List the library sub-modules that are used by the application + +LGW_INC = $(LGW_PATH)/inc/config.h +LGW_INC += $(LGW_PATH)/inc/loragw_hal.h + +### Linking options + +LIBS := -lloragw -lrt -lm + +### General build targets + +all: $(APP_NAME) + +clean: + rm -f $(OBJDIR)/*.o + rm -f $(APP_NAME) + +### HAL library (do no force multiple library rebuild even with 'make -B') + +$(LGW_PATH)/inc/config.h: + @if test ! -f $@; then \ + $(MAKE) all -C $(LGW_PATH); \ + fi + +$(LGW_PATH)/libloragw.a: $(LGW_INC) + @if test ! -f $@; then \ + $(MAKE) all -C $(LGW_PATH); \ + fi + +### Sub-modules compilation + +$(OBJDIR): + mkdir -p $(OBJDIR) + +$(OBJDIR)/parson.o: src/parson.c inc/parson.h | $(OBJDIR) + $(CC) -c $(CFLAGS) $< -o $@ + +### Main program compilation and assembly + +$(OBJDIR)/$(APP_NAME).o: src/$(APP_NAME).c $(LGW_INC) inc/parson.h | $(OBJDIR) + $(CC) -c $(CFLAGS) -I$(LGW_PATH)/inc $< -o $@ + +$(APP_NAME): $(OBJDIR)/$(APP_NAME).o $(LGW_PATH)/libloragw.a $(OBJDIR)/parson.o + $(CC) -L$(LGW_PATH) $< $(OBJDIR)/parson.o -o $@ $(LIBS) + +### EOF diff --git a/util_pkt_logger/global_conf.json b/util_pkt_logger/global_conf.json new file mode 100644 index 0000000..a3f327a --- /dev/null +++ b/util_pkt_logger/global_conf.json @@ -0,0 +1,87 @@ +{ + "SX1301_conf": { + "lorawan_public": true, + "clksrc": 1, /* radio_1 provides clock to concentrator */ + "radio_0": { + "enable": true, + "type": "SX1257", + "freq": 867500000, + "rssi_offset": -166.0, + "tx_enable": false + }, + "radio_1": { + "enable": true, + "type": "SX1257", + "freq": 868500000, + "rssi_offset": -166.0, + "tx_enable": false + }, + "chan_multiSF_0": { + /* Lora MAC channel, 125kHz, all SF, 868.1 MHz */ + "enable": true, + "radio": 1, + "if": -400000 + }, + "chan_multiSF_1": { + /* Lora MAC channel, 125kHz, all SF, 868.3 MHz */ + "enable": true, + "radio": 1, + "if": -200000 + }, + "chan_multiSF_2": { + /* Lora MAC channel, 125kHz, all SF, 868.5 MHz */ + "enable": true, + "radio": 1, + "if": 0 + }, + "chan_multiSF_3": { + /* Lora MAC channel, 125kHz, all SF, 867.1 MHz */ + "enable": true, + "radio": 0, + "if": -400000 + }, + "chan_multiSF_4": { + /* Lora MAC channel, 125kHz, all SF, 867.3 MHz */ + "enable": true, + "radio": 0, + "if": -200000 + }, + "chan_multiSF_5": { + /* Lora MAC channel, 125kHz, all SF, 867.5 MHz */ + "enable": true, + "radio": 0, + "if": 0 + }, + "chan_multiSF_6": { + /* Lora MAC channel, 125kHz, all SF, 867.7 MHz */ + "enable": true, + "radio": 0, + "if": 200000 + }, + "chan_multiSF_7": { + /* Lora MAC channel, 125kHz, all SF, 867.9 MHz */ + "enable": true, + "radio": 0, + "if": 400000 + }, + "chan_Lora_std": { + /* Lora MAC channel, 250kHz, SF7, 868.3 MHz */ + "enable": true, + "radio": 1, + "if": -200000, + "bandwidth": 250000, + "spread_factor": 7 + }, + "chan_FSK": { + /* FSK 50kbps channel, 868.8 MHz */ + "enable": true, + "radio": 1, + "if": 300000, + "bandwidth": 125000, + "datarate": 50000 + } + }, + "gateway_conf": { + "gateway_ID": "AA555A0000000000" + } +} diff --git a/util_pkt_logger/inc/parson.h b/util_pkt_logger/inc/parson.h new file mode 100644 index 0000000..2669a18 --- /dev/null +++ b/util_pkt_logger/inc/parson.h @@ -0,0 +1,222 @@ +/* + Parson ( http://kgabis.github.com/parson/ ) + Copyright (c) 2012 - 2016 Krzysztof Gabis + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + 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 NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef parson_parson_h +#define parson_parson_h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <stddef.h> /* size_t */ + +/* Types and enums */ +typedef struct json_object_t JSON_Object; +typedef struct json_array_t JSON_Array; +typedef struct json_value_t JSON_Value; + +enum json_value_type { + JSONError = -1, + JSONNull = 1, + JSONString = 2, + JSONNumber = 3, + JSONObject = 4, + JSONArray = 5, + JSONBoolean = 6 +}; +typedef int JSON_Value_Type; + +enum json_result_t { + JSONSuccess = 0, + JSONFailure = -1 +}; +typedef int JSON_Status; + +typedef void * (*JSON_Malloc_Function)(size_t); +typedef void (*JSON_Free_Function)(void *); + +/* Call only once, before calling any other function from parson API. If not called, malloc and free + from stdlib will be used for all allocations */ +void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun); + +/* Parses first JSON value in a file, returns NULL in case of error */ +JSON_Value * json_parse_file(const char *filename); + +/* Parses first JSON value in a file and ignores comments (/ * * / and //), + returns NULL in case of error */ +JSON_Value * json_parse_file_with_comments(const char *filename); + +/* Parses first JSON value in a string, returns NULL in case of error */ +JSON_Value * json_parse_string(const char *string); + +/* Parses first JSON value in a string and ignores comments (/ * * / and //), + returns NULL in case of error */ +JSON_Value * json_parse_string_with_comments(const char *string); + +/* Serialization */ +size_t json_serialization_size(const JSON_Value *value); /* returns 0 on fail */ +JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes); +JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename); +char * json_serialize_to_string(const JSON_Value *value); + +/* Pretty serialization */ +size_t json_serialization_size_pretty(const JSON_Value *value); /* returns 0 on fail */ +JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes); +JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename); +char * json_serialize_to_string_pretty(const JSON_Value *value); + +void json_free_serialized_string(char *string); /* frees string from json_serialize_to_string and json_serialize_to_string_pretty */ + +/* Comparing */ +int json_value_equals(const JSON_Value *a, const JSON_Value *b); + +/* Validation + This is *NOT* JSON Schema. It validates json by checking if object have identically + named fields with matching types. + For example schema {"name":"", "age":0} will validate + {"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"}, + but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}. + In case of arrays, only first value in schema is checked against all values in tested array. + Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays, + null validates values of every type. + */ +JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value); + +/* + * JSON Object + */ +JSON_Value * json_object_get_value (const JSON_Object *object, const char *name); +const char * json_object_get_string (const JSON_Object *object, const char *name); +JSON_Object * json_object_get_object (const JSON_Object *object, const char *name); +JSON_Array * json_object_get_array (const JSON_Object *object, const char *name); +double json_object_get_number (const JSON_Object *object, const char *name); /* returns 0 on fail */ +int json_object_get_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */ + +/* dotget functions enable addressing values with dot notation in nested objects, + just like in structs or c++/java/c# objects (e.g. objectA.objectB.value). + Because valid names in JSON can contain dots, some values may be inaccessible + this way. */ +JSON_Value * json_object_dotget_value (const JSON_Object *object, const char *name); +const char * json_object_dotget_string (const JSON_Object *object, const char *name); +JSON_Object * json_object_dotget_object (const JSON_Object *object, const char *name); +JSON_Array * json_object_dotget_array (const JSON_Object *object, const char *name); +double json_object_dotget_number (const JSON_Object *object, const char *name); /* returns 0 on fail */ +int json_object_dotget_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */ + +/* Functions to get available names */ +size_t json_object_get_count(const JSON_Object *object); +const char * json_object_get_name (const JSON_Object *object, size_t index); + +/* Creates new name-value pair or frees and replaces old value with a new one. + * json_object_set_value does not copy passed value so it shouldn't be freed afterwards. */ +JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value); +JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string); +JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number); +JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean); +JSON_Status json_object_set_null(JSON_Object *object, const char *name); + +/* Works like dotget functions, but creates whole hierarchy if necessary. + * json_object_dotset_value does not copy passed value so it shouldn't be freed afterwards. */ +JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value); +JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string); +JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number); +JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean); +JSON_Status json_object_dotset_null(JSON_Object *object, const char *name); + +/* Frees and removes name-value pair */ +JSON_Status json_object_remove(JSON_Object *object, const char *name); + +/* Works like dotget function, but removes name-value pair only on exact match. */ +JSON_Status json_object_dotremove(JSON_Object *object, const char *key); + +/* Removes all name-value pairs in object */ +JSON_Status json_object_clear(JSON_Object *object); + +/* + *JSON Array + */ +JSON_Value * json_array_get_value (const JSON_Array *array, size_t index); +const char * json_array_get_string (const JSON_Array *array, size_t index); +JSON_Object * json_array_get_object (const JSON_Array *array, size_t index); +JSON_Array * json_array_get_array (const JSON_Array *array, size_t index); +double json_array_get_number (const JSON_Array *array, size_t index); /* returns 0 on fail */ +int json_array_get_boolean(const JSON_Array *array, size_t index); /* returns -1 on fail */ +size_t json_array_get_count (const JSON_Array *array); + +/* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist. + * Order of values in array may change during execution. */ +JSON_Status json_array_remove(JSON_Array *array, size_t i); + +/* Frees and removes from array value at given index and replaces it with given one. + * Does nothing and returns JSONFailure if index doesn't exist. + * json_array_replace_value does not copy passed value so it shouldn't be freed afterwards. */ +JSON_Status json_array_replace_value(JSON_Array *array, size_t i, JSON_Value *value); +JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string); +JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number); +JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean); +JSON_Status json_array_replace_null(JSON_Array *array, size_t i); + +/* Frees and removes all values from array */ +JSON_Status json_array_clear(JSON_Array *array); + +/* Appends new value at the end of array. + * json_array_append_value does not copy passed value so it shouldn't be freed afterwards. */ +JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value); +JSON_Status json_array_append_string(JSON_Array *array, const char *string); +JSON_Status json_array_append_number(JSON_Array *array, double number); +JSON_Status json_array_append_boolean(JSON_Array *array, int boolean); +JSON_Status json_array_append_null(JSON_Array *array); + +/* + *JSON Value + */ +JSON_Value * json_value_init_object (void); +JSON_Value * json_value_init_array (void); +JSON_Value * json_value_init_string (const char *string); /* copies passed string */ +JSON_Value * json_value_init_number (double number); +JSON_Value * json_value_init_boolean(int boolean); +JSON_Value * json_value_init_null (void); +JSON_Value * json_value_deep_copy (const JSON_Value *value); +void json_value_free (JSON_Value *value); + +JSON_Value_Type json_value_get_type (const JSON_Value *value); +JSON_Object * json_value_get_object (const JSON_Value *value); +JSON_Array * json_value_get_array (const JSON_Value *value); +const char * json_value_get_string (const JSON_Value *value); +double json_value_get_number (const JSON_Value *value); +int json_value_get_boolean(const JSON_Value *value); + +/* Same as above, but shorter */ +JSON_Value_Type json_type (const JSON_Value *value); +JSON_Object * json_object (const JSON_Value *value); +JSON_Array * json_array (const JSON_Value *value); +const char * json_string (const JSON_Value *value); +double json_number (const JSON_Value *value); +int json_boolean(const JSON_Value *value); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/util_pkt_logger/local_conf.json b/util_pkt_logger/local_conf.json new file mode 100644 index 0000000..51fc7c2 --- /dev/null +++ b/util_pkt_logger/local_conf.json @@ -0,0 +1,5 @@ +{ + "gateway_conf": { + "gateway_ID": "AA555A0000000101" + } +} diff --git a/util_pkt_logger/readme.md b/util_pkt_logger/readme.md new file mode 100644 index 0000000..a980bd4 --- /dev/null +++ b/util_pkt_logger/readme.md @@ -0,0 +1,143 @@ + / _____) _ | | + ( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | + (______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +LoRa packet logger +=================== + +1. Introduction +---------------- + +This software is used to set up a LoRa concentrator using a JSON configuration +file and then record all the packets received in a log file, indefinitely, until +the user stops the application. +No filtering is done and all packets that are LoRa packets with the correct RF +parameters (frequency, datarate, bandwidth) should appear in the log. + +2. Dependencies +---------------- + +This program uses the Parson library (http://kgabis.github.com/parson/) by +Krzysztof Gabis for JSON parsing. +Many thanks to him for that very practical and well written library. + +This program is a typical example of LoRa concentrator HAL usage for receiving +packets. + +Only high-level functions are used (the ones contained in loragw_hal) so there +is no hardware dependencies assuming the HAL is matched with the proper version +of the hardware. +Data structures of the received packets are accessed by name (ie. not at a +binary level) so new functionalities can be added to the API without affecting +that program at all. + +It was tested with v1.3.0 of the libloragw library, and should be compatible +with any later version of the library assuming the API is downward-compatible. + +3. Usage +--------- + +To stop the application, press Ctrl+C. + +The only optional parameter when launching the application is the log rotation +time (in seconds). + +The way the program takes configuration files into account is the following: + * if there is a debug_conf.json parse it, others are ignored + * if there is a global_conf.json parse it and look for the next file + * if there is a local_conf.json parse it +If some parameters are defined in both global and local configuration files, the +local definition overwrites the global definition. + +The global configuration file should be exactly the same throughout your +network, contain all global parameters (parameters for "sensor" radio channels) +and preferably default "safe" values for parameters that are specific for each +gateway (eg. specify a default MAC address). + +If you have build the libloragw library for a specific radio band (eg. ETSI +868 MHz band) a ready-to-use global_conf.json file is generated by the Makefile +with a set of channels typical for a 'LoRa MAC' network application. +If you don't specify a radio band, an empty global_conf.json is generated and +must be filled with the settings you need. + +The local configuration file should contain parameters that are specific to each +gateway (eg. MAC address, frequency for backhaul radio channels). + +In each configuration file, the program looks for a JSON object named +"SX1301_conf" that should contain the parameters for the LoRa concentrator board +(RF channels definition, modem parameters, etc) and another JSON object called +"gateway_conf" that should contain the gateway parameters (gateway MAC address, +IP address of the LoRa MAC controller, network authentication parameters, etc). + +To learn more about the JSON configuration format, read the provided JSON files +and the API documentation. A dedicated document will be available later on. + +The received packets are put in a CSV file whose name include the MAC address of +the gateway in hexadecimal format and a UTC timestamp of log starting time in +ISO 8601 recommended compact format: +yyyymmddThhmmssZ (eg. 20131009T172345Z for October 9th, 2013 at 5:23:45PM UTC) + +To able continuous monitoring, the current log file is closed is closed and a +new one is opened every hour (by default, rotation interval is settable by the +user using -r command line option). +No packet is lost during that rotation of log file. +Every log file but the current one can then be modified, uploaded and/or deleted +without any consequence for the program execution. + +4. License +----------- + +Copyright (c) 2013, SEMTECH S.A. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of the Semtech corporation nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +5. License for Parson library +------------------------------ + +Parson ( http://kgabis.github.com/parson/ ) +Copyright (c) 2012 Krzysztof Gabis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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 NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*EOF*
\ No newline at end of file diff --git a/util_pkt_logger/src/parson.c b/util_pkt_logger/src/parson.c new file mode 100644 index 0000000..16bb158 --- /dev/null +++ b/util_pkt_logger/src/parson.c @@ -0,0 +1,1765 @@ +/* + Parson ( http://kgabis.github.com/parson/ ) + Copyright (c) 2012 - 2016 Krzysztof Gabis + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + 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 NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "parson.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <math.h> + +#define STARTING_CAPACITY 15 +#define ARRAY_MAX_CAPACITY 122880 /* 15*(2^13) */ +#define OBJECT_MAX_CAPACITY 960 /* 15*(2^6) */ +#define MAX_NESTING 19 +#define DOUBLE_SERIALIZATION_FORMAT "%f" + +#define SIZEOF_TOKEN(a) (sizeof(a) - 1) +#define SKIP_CHAR(str) ((*str)++) +#define SKIP_WHITESPACES(str) while (isspace(**str)) { SKIP_CHAR(str); } +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#undef malloc +#undef free + +static JSON_Malloc_Function parson_malloc = malloc; +static JSON_Free_Function parson_free = free; + +#define IS_CONT(b) (((unsigned char)(b) & 0xC0) == 0x80) /* is utf-8 continuation byte */ + +/* Type definitions */ +typedef union json_value_value { + char *string; + double number; + JSON_Object *object; + JSON_Array *array; + int boolean; + int null; +} JSON_Value_Value; + +struct json_value_t { + JSON_Value_Type type; + JSON_Value_Value value; +}; + +struct json_object_t { + char **names; + JSON_Value **values; + size_t count; + size_t capacity; +}; + +struct json_array_t { + JSON_Value **items; + size_t count; + size_t capacity; +}; + +/* Various */ +static char * read_file(const char *filename); +static void remove_comments(char *string, const char *start_token, const char *end_token); +static char * parson_strndup(const char *string, size_t n); +static char * parson_strdup(const char *string); +static int is_utf16_hex(const unsigned char *string); +static int num_bytes_in_utf8_sequence(unsigned char c); +static int verify_utf8_sequence(const unsigned char *string, int *len); +static int is_valid_utf8(const char *string, size_t string_len); +static int is_decimal(const char *string, size_t length); + +/* JSON Object */ +static JSON_Object * json_object_init(void); +static JSON_Status json_object_add(JSON_Object *object, const char *name, JSON_Value *value); +static JSON_Status json_object_resize(JSON_Object *object, size_t new_capacity); +static JSON_Value * json_object_nget_value(const JSON_Object *object, const char *name, size_t n); +static void json_object_free(JSON_Object *object); + +/* JSON Array */ +static JSON_Array * json_array_init(void); +static JSON_Status json_array_add(JSON_Array *array, JSON_Value *value); +static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity); +static void json_array_free(JSON_Array *array); + +/* JSON Value */ +static JSON_Value * json_value_init_string_no_copy(char *string); + +/* Parser */ +static void skip_quotes(const char **string); +static int parse_utf_16(const char **unprocessed, char **processed); +static char * process_string(const char *input, size_t len); +static char * get_quoted_string(const char **string); +static JSON_Value * parse_object_value(const char **string, size_t nesting); +static JSON_Value * parse_array_value(const char **string, size_t nesting); +static JSON_Value * parse_string_value(const char **string); +static JSON_Value * parse_boolean_value(const char **string); +static JSON_Value * parse_number_value(const char **string); +static JSON_Value * parse_null_value(const char **string); +static JSON_Value * parse_value(const char **string, size_t nesting); + +/* Serialization */ +static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, int is_pretty, char *num_buf); +static int json_serialize_string(const char *string, char *buf); +static int append_indent(char *buf, int level); +static int append_string(char *buf, const char *string); + +/* Various */ +static char * parson_strndup(const char *string, size_t n) { + char *output_string = (char*)parson_malloc(n + 1); + if (!output_string) + return NULL; + output_string[n] = '\0'; + strncpy(output_string, string, n); + return output_string; +} + +static char * parson_strdup(const char *string) { + return parson_strndup(string, strlen(string)); +} + +static int is_utf16_hex(const unsigned char *s) { + return isxdigit(s[0]) && isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]); +} + +static int num_bytes_in_utf8_sequence(unsigned char c) { + if (c == 0xC0 || c == 0xC1 || c > 0xF4 || IS_CONT(c)) { + return 0; + } else if ((c & 0x80) == 0) { /* 0xxxxxxx */ + return 1; + } else if ((c & 0xE0) == 0xC0) { /* 110xxxxx */ + return 2; + } else if ((c & 0xF0) == 0xE0) { /* 1110xxxx */ + return 3; + } else if ((c & 0xF8) == 0xF0) { /* 11110xxx */ + return 4; + } + return 0; /* won't happen */ +} + +static int verify_utf8_sequence(const unsigned char *string, int *len) { + unsigned int cp = 0; + *len = num_bytes_in_utf8_sequence(string[0]); + + if (*len == 1) { + cp = string[0]; + } else if (*len == 2 && IS_CONT(string[1])) { + cp = string[0] & 0x1F; + cp = (cp << 6) | (string[1] & 0x3F); + } else if (*len == 3 && IS_CONT(string[1]) && IS_CONT(string[2])) { + cp = ((unsigned char)string[0]) & 0xF; + cp = (cp << 6) | (string[1] & 0x3F); + cp = (cp << 6) | (string[2] & 0x3F); + } else if (*len == 4 && IS_CONT(string[1]) && IS_CONT(string[2]) && IS_CONT(string[3])) { + cp = string[0] & 0x7; + cp = (cp << 6) | (string[1] & 0x3F); + cp = (cp << 6) | (string[2] & 0x3F); + cp = (cp << 6) | (string[3] & 0x3F); + } else { + return 0; + } + + /* overlong encodings */ + if ((cp < 0x80 && *len > 1) || + (cp < 0x800 && *len > 2) || + (cp < 0x10000 && *len > 3)) { + return 0; + } + + /* invalid unicode */ + if (cp > 0x10FFFF) { + return 0; + } + + /* surrogate halves */ + if (cp >= 0xD800 && cp <= 0xDFFF) { + return 0; + } + + return 1; +} + +static int is_valid_utf8(const char *string, size_t string_len) { + int len = 0; + const char *string_end = string + string_len; + while (string < string_end) { + if (!verify_utf8_sequence((const unsigned char*)string, &len)) { + return 0; + } + string += len; + } + return 1; +} + +static int is_decimal(const char *string, size_t length) { + if (length > 1 && string[0] == '0' && string[1] != '.') + return 0; + if (length > 2 && !strncmp(string, "-0", 2) && string[2] != '.') + return 0; + while (length--) + if (strchr("xX", string[length])) + return 0; + return 1; +} + +static char * read_file(const char * filename) { + FILE *fp = fopen(filename, "r"); + size_t file_size; + long pos; + char *file_contents; + if (!fp) + return NULL; + fseek(fp, 0L, SEEK_END); + pos = ftell(fp); + if (pos < 0) { + fclose(fp); + return NULL; + } + file_size = pos; + rewind(fp); + file_contents = (char*)parson_malloc(sizeof(char) * (file_size + 1)); + if (!file_contents) { + fclose(fp); + return NULL; + } + if (fread(file_contents, file_size, 1, fp) < 1) { + if (ferror(fp)) { + fclose(fp); + parson_free(file_contents); + return NULL; + } + } + fclose(fp); + file_contents[file_size] = '\0'; + return file_contents; +} + +static void remove_comments(char *string, const char *start_token, const char *end_token) { + int in_string = 0, escaped = 0; + size_t i; + char *ptr = NULL, current_char; + size_t start_token_len = strlen(start_token); + size_t end_token_len = strlen(end_token); + if (start_token_len == 0 || end_token_len == 0) + return; + while ((current_char = *string) != '\0') { + if (current_char == '\\' && !escaped) { + escaped = 1; + string++; + continue; + } else if (current_char == '\"' && !escaped) { + in_string = !in_string; + } else if (!in_string && strncmp(string, start_token, start_token_len) == 0) { + for(i = 0; i < start_token_len; i++) + string[i] = ' '; + string = string + start_token_len; + ptr = strstr(string, end_token); + if (!ptr) + return; + for (i = 0; i < (ptr - string) + end_token_len; i++) + string[i] = ' '; + string = ptr + end_token_len - 1; + } + escaped = 0; + string++; + } +} + +/* JSON Object */ +static JSON_Object * json_object_init(void) { + JSON_Object *new_obj = (JSON_Object*)parson_malloc(sizeof(JSON_Object)); + if (!new_obj) + return NULL; + new_obj->names = (char**)NULL; + new_obj->values = (JSON_Value**)NULL; + new_obj->capacity = 0; + new_obj->count = 0; + return new_obj; +} + +static JSON_Status json_object_add(JSON_Object *object, const char *name, JSON_Value *value) { + size_t index = 0; + if (object == NULL || name == NULL || value == NULL) { + return JSONFailure; + } + if (object->count >= object->capacity) { + size_t new_capacity = MAX(object->capacity * 2, STARTING_CAPACITY); + if (new_capacity > OBJECT_MAX_CAPACITY) + return JSONFailure; + if (json_object_resize(object, new_capacity) == JSONFailure) + return JSONFailure; + } + if (json_object_get_value(object, name) != NULL) + return JSONFailure; + index = object->count; + object->names[index] = parson_strdup(name); + if (object->names[index] == NULL) + return JSONFailure; + object->values[index] = value; + object->count++; + return JSONSuccess; +} + +static JSON_Status json_object_resize(JSON_Object *object, size_t new_capacity) { + char **temp_names = NULL; + JSON_Value **temp_values = NULL; + + if ((object->names == NULL && object->values != NULL) || + (object->names != NULL && object->values == NULL) || + new_capacity == 0) { + return JSONFailure; /* Shouldn't happen */ + } + + temp_names = (char**)parson_malloc(new_capacity * sizeof(char*)); + if (temp_names == NULL) + return JSONFailure; + + temp_values = (JSON_Value**)parson_malloc(new_capacity * sizeof(JSON_Value*)); + if (temp_values == NULL) { + parson_free(temp_names); + return JSONFailure; + } + + if (object->names != NULL && object->values != NULL && object->count > 0) { + memcpy(temp_names, object->names, object->count * sizeof(char*)); + memcpy(temp_values, object->values, object->count * sizeof(JSON_Value*)); + } + parson_free(object->names); + parson_free(object->values); + object->names = temp_names; + object->values = temp_values; + object->capacity = new_capacity; + return JSONSuccess; +} + +static JSON_Value * json_object_nget_value(const JSON_Object *object, const char *name, size_t n) { + size_t i, name_length; + for (i = 0; i < json_object_get_count(object); i++) { + name_length = strlen(object->names[i]); + if (name_length != n) + continue; + if (strncmp(object->names[i], name, n) == 0) + return object->values[i]; + } + return NULL; +} + +static void json_object_free(JSON_Object *object) { + while(object->count--) { + parson_free(object->names[object->count]); + json_value_free(object->values[object->count]); + } + parson_free(object->names); + parson_free(object->values); + parson_free(object); +} + +/* JSON Array */ +static JSON_Array * json_array_init(void) { + JSON_Array *new_array = (JSON_Array*)parson_malloc(sizeof(JSON_Array)); + if (!new_array) + return NULL; + new_array->items = (JSON_Value**)NULL; + new_array->capacity = 0; + new_array->count = 0; + return new_array; +} + +static JSON_Status json_array_add(JSON_Array *array, JSON_Value *value) { + if (array->count >= array->capacity) { + size_t new_capacity = MAX(array->capacity * 2, STARTING_CAPACITY); + if (new_capacity > ARRAY_MAX_CAPACITY) + return JSONFailure; + if (json_array_resize(array, new_capacity) == JSONFailure) + return JSONFailure; + } + array->items[array->count] = value; + array->count++; + return JSONSuccess; +} + +static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity) { + JSON_Value **new_items = NULL; + if (new_capacity == 0) { + return JSONFailure; + } + new_items = (JSON_Value**)parson_malloc(new_capacity * sizeof(JSON_Value*)); + if (new_items == NULL) { + return JSONFailure; + } + if (array->items != NULL && array->count > 0) { + memcpy(new_items, array->items, array->count * sizeof(JSON_Value*)); + } + parson_free(array->items); + array->items = new_items; + array->capacity = new_capacity; + return JSONSuccess; +} + +static void json_array_free(JSON_Array *array) { + while (array->count--) + json_value_free(array->items[array->count]); + parson_free(array->items); + parson_free(array); +} + +/* JSON Value */ +static JSON_Value * json_value_init_string_no_copy(char *string) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) + return NULL; + new_value->type = JSONString; + new_value->value.string = string; + return new_value; +} + +/* Parser */ +static void skip_quotes(const char **string) { + SKIP_CHAR(string); + while (**string != '\"') { + if (**string == '\0') + return; + if (**string == '\\') { + SKIP_CHAR(string); + if (**string == '\0') + return; + } + SKIP_CHAR(string); + } + SKIP_CHAR(string); +} + +static int parse_utf_16(const char **unprocessed, char **processed) { + unsigned int cp, lead, trail; + char *processed_ptr = *processed; + const char *unprocessed_ptr = *unprocessed; + unprocessed_ptr++; /* skips u */ + if (!is_utf16_hex((const unsigned char*)unprocessed_ptr) || sscanf(unprocessed_ptr, "%4x", &cp) == EOF) + return JSONFailure; + if (cp < 0x80) { + *processed_ptr = cp; /* 0xxxxxxx */ + } else if (cp < 0x800) { + *processed_ptr++ = ((cp >> 6) & 0x1F) | 0xC0; /* 110xxxxx */ + *processed_ptr = ((cp ) & 0x3F) | 0x80; /* 10xxxxxx */ + } else if (cp < 0xD800 || cp > 0xDFFF) { + *processed_ptr++ = ((cp >> 12) & 0x0F) | 0xE0; /* 1110xxxx */ + *processed_ptr++ = ((cp >> 6) & 0x3F) | 0x80; /* 10xxxxxx */ + *processed_ptr = ((cp ) & 0x3F) | 0x80; /* 10xxxxxx */ + } else if (cp >= 0xD800 && cp <= 0xDBFF) { /* lead surrogate (0xD800..0xDBFF) */ + lead = cp; + unprocessed_ptr += 4; /* should always be within the buffer, otherwise previous sscanf would fail */ + if (*unprocessed_ptr++ != '\\' || *unprocessed_ptr++ != 'u' || /* starts with \u? */ + !is_utf16_hex((const unsigned char*)unprocessed_ptr) || + sscanf(unprocessed_ptr, "%4x", &trail) == EOF || + trail < 0xDC00 || trail > 0xDFFF) { /* valid trail surrogate? (0xDC00..0xDFFF) */ + return JSONFailure; + } + cp = ((((lead-0xD800)&0x3FF)<<10)|((trail-0xDC00)&0x3FF))+0x010000; + *processed_ptr++ = (((cp >> 18) & 0x07) | 0xF0); /* 11110xxx */ + *processed_ptr++ = (((cp >> 12) & 0x3F) | 0x80); /* 10xxxxxx */ + *processed_ptr++ = (((cp >> 6) & 0x3F) | 0x80); /* 10xxxxxx */ + *processed_ptr = (((cp ) & 0x3F) | 0x80); /* 10xxxxxx */ + } else { /* trail surrogate before lead surrogate */ + return JSONFailure; + } + unprocessed_ptr += 3; + *processed = processed_ptr; + *unprocessed = unprocessed_ptr; + return JSONSuccess; +} + + +/* Copies and processes passed string up to supplied length. +Example: "\u006Corem ipsum" -> lorem ipsum */ +static char* process_string(const char *input, size_t len) { + const char *input_ptr = input; + size_t initial_size = (len + 1) * sizeof(char); + size_t final_size = 0; + char *output = (char*)parson_malloc(initial_size); + char *output_ptr = output; + char *resized_output = NULL; + while ((*input_ptr != '\0') && (size_t)(input_ptr - input) < len) { + if (*input_ptr == '\\') { + input_ptr++; + switch (*input_ptr) { + case '\"': *output_ptr = '\"'; break; + case '\\': *output_ptr = '\\'; break; + case '/': *output_ptr = '/'; break; + case 'b': *output_ptr = '\b'; break; + case 'f': *output_ptr = '\f'; break; + case 'n': *output_ptr = '\n'; break; + case 'r': *output_ptr = '\r'; break; + case 't': *output_ptr = '\t'; break; + case 'u': + if (parse_utf_16(&input_ptr, &output_ptr) == JSONFailure) + goto error; + break; + default: + goto error; + } + } else if ((unsigned char)*input_ptr < 0x20) { + goto error; /* 0x00-0x19 are invalid characters for json string (http://www.ietf.org/rfc/rfc4627.txt) */ + } else { + *output_ptr = *input_ptr; + } + output_ptr++; + input_ptr++; + } + *output_ptr = '\0'; + /* resize to new length */ + final_size = (size_t)(output_ptr-output) + 1; + resized_output = (char*)parson_malloc(final_size); + if (resized_output == NULL) + goto error; + memcpy(resized_output, output, final_size); + parson_free(output); + return resized_output; +error: + parson_free(output); + return NULL; +} + +/* Return processed contents of a string between quotes and + skips passed argument to a matching quote. */ +static char * get_quoted_string(const char **string) { + const char *string_start = *string; + size_t string_len = 0; + skip_quotes(string); + if (**string == '\0') + return NULL; + string_len = *string - string_start - 2; /* length without quotes */ + return process_string(string_start + 1, string_len); +} + +static JSON_Value * parse_value(const char **string, size_t nesting) { + if (nesting > MAX_NESTING) + return NULL; + SKIP_WHITESPACES(string); + switch (**string) { + case '{': + return parse_object_value(string, nesting + 1); + case '[': + return parse_array_value(string, nesting + 1); + case '\"': + return parse_string_value(string); + case 'f': case 't': + return parse_boolean_value(string); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return parse_number_value(string); + case 'n': + return parse_null_value(string); + default: + return NULL; + } +} + +static JSON_Value * parse_object_value(const char **string, size_t nesting) { + JSON_Value *output_value = json_value_init_object(), *new_value = NULL; + JSON_Object *output_object = json_value_get_object(output_value); + char *new_key = NULL; + if (output_value == NULL) + return NULL; + SKIP_CHAR(string); + SKIP_WHITESPACES(string); + if (**string == '}') { /* empty object */ + SKIP_CHAR(string); + return output_value; + } + while (**string != '\0') { + new_key = get_quoted_string(string); + SKIP_WHITESPACES(string); + if (new_key == NULL || **string != ':') { + json_value_free(output_value); + return NULL; + } + SKIP_CHAR(string); + new_value = parse_value(string, nesting); + if (new_value == NULL) { + parson_free(new_key); + json_value_free(output_value); + return NULL; + } + if(json_object_add(output_object, new_key, new_value) == JSONFailure) { + parson_free(new_key); + parson_free(new_value); + json_value_free(output_value); + return NULL; + } + parson_free(new_key); + SKIP_WHITESPACES(string); + if (**string != ',') + break; + SKIP_CHAR(string); + SKIP_WHITESPACES(string); + } + SKIP_WHITESPACES(string); + if (**string != '}' || /* Trim object after parsing is over */ + json_object_resize(output_object, json_object_get_count(output_object)) == JSONFailure) { + json_value_free(output_value); + return NULL; + } + SKIP_CHAR(string); + return output_value; +} + +static JSON_Value * parse_array_value(const char **string, size_t nesting) { + JSON_Value *output_value = json_value_init_array(), *new_array_value = NULL; + JSON_Array *output_array = json_value_get_array(output_value); + if (!output_value) + return NULL; + SKIP_CHAR(string); + SKIP_WHITESPACES(string); + if (**string == ']') { /* empty array */ + SKIP_CHAR(string); + return output_value; + } + while (**string != '\0') { + new_array_value = parse_value(string, nesting); + if (!new_array_value) { + json_value_free(output_value); + return NULL; + } + if(json_array_add(output_array, new_array_value) == JSONFailure) { + parson_free(new_array_value); + json_value_free(output_value); + return NULL; + } + SKIP_WHITESPACES(string); + if (**string != ',') + break; + SKIP_CHAR(string); + SKIP_WHITESPACES(string); + } + SKIP_WHITESPACES(string); + if (**string != ']' || /* Trim array after parsing is over */ + json_array_resize(output_array, json_array_get_count(output_array)) == JSONFailure) { + json_value_free(output_value); + return NULL; + } + SKIP_CHAR(string); + return output_value; +} + +static JSON_Value * parse_string_value(const char **string) { + JSON_Value *value = NULL; + char *new_string = get_quoted_string(string); + if (new_string == NULL) + return NULL; + value = json_value_init_string_no_copy(new_string); + if (value == NULL) { + parson_free(new_string); + return NULL; + } + return value; +} + +static JSON_Value * parse_boolean_value(const char **string) { + size_t true_token_size = SIZEOF_TOKEN("true"); + size_t false_token_size = SIZEOF_TOKEN("false"); + if (strncmp("true", *string, true_token_size) == 0) { + *string += true_token_size; + return json_value_init_boolean(1); + } else if (strncmp("false", *string, false_token_size) == 0) { + *string += false_token_size; + return json_value_init_boolean(0); + } + return NULL; +} + +static JSON_Value * parse_number_value(const char **string) { + char *end; + double number = strtod(*string, &end); + JSON_Value *output_value; + if (is_decimal(*string, end - *string)) { + *string = end; + output_value = json_value_init_number(number); + } else { + output_value = NULL; + } + return output_value; +} + +static JSON_Value * parse_null_value(const char **string) { + size_t token_size = SIZEOF_TOKEN("null"); + if (strncmp("null", *string, token_size) == 0) { + *string += token_size; + return json_value_init_null(); + } + return NULL; +} + +/* Serialization */ +#define APPEND_STRING(str) do { written = append_string(buf, (str)); \ + if (written < 0) { return -1; } \ + if (buf != NULL) { buf += written; } \ + written_total += written; } while(0) + +#define APPEND_INDENT(level) do { written = append_indent(buf, (level)); \ + if (written < 0) { return -1; } \ + if (buf != NULL) { buf += written; } \ + written_total += written; } while(0) + +static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, int is_pretty, char *num_buf) +{ + const char *key = NULL, *string = NULL; + JSON_Value *temp_value = NULL; + JSON_Array *array = NULL; + JSON_Object *object = NULL; + size_t i = 0, count = 0; + double num = 0.0; + int written = -1, written_total = 0; + + switch (json_value_get_type(value)) { + case JSONArray: + array = json_value_get_array(value); + count = json_array_get_count(array); + APPEND_STRING("["); + if (count > 0 && is_pretty) + APPEND_STRING("\n"); + for (i = 0; i < count; i++) { + if (is_pretty) + APPEND_INDENT(level+1); + temp_value = json_array_get_value(array, i); + written = json_serialize_to_buffer_r(temp_value, buf, level+1, is_pretty, num_buf); + if (written < 0) + return -1; + if (buf != NULL) + buf += written; + written_total += written; + if (i < (count - 1)) + APPEND_STRING(","); + if (is_pretty) + APPEND_STRING("\n"); + } + if (count > 0 && is_pretty) + APPEND_INDENT(level); + APPEND_STRING("]"); + return written_total; + case JSONObject: + object = json_value_get_object(value); + count = json_object_get_count(object); + APPEND_STRING("{"); + if (count > 0 && is_pretty) + APPEND_STRING("\n"); + for (i = 0; i < count; i++) { + key = json_object_get_name(object, i); + if (is_pretty) + APPEND_INDENT(level+1); + written = json_serialize_string(key, buf); + if (written < 0) + return -1; + if (buf != NULL) + buf += written; + written_total += written; + APPEND_STRING(":"); + if (is_pretty) + APPEND_STRING(" "); + temp_value = json_object_get_value(object, key); + written = json_serialize_to_buffer_r(temp_value, buf, level+1, is_pretty, num_buf); + if (written < 0) + return -1; + if (buf != NULL) + buf += written; + written_total += written; + if (i < (count - 1)) + APPEND_STRING(","); + if (is_pretty) + APPEND_STRING("\n"); + } + if (count > 0 && is_pretty) + APPEND_INDENT(level); + APPEND_STRING("}"); + return written_total; + case JSONString: + string = json_value_get_string(value); + written = json_serialize_string(string, buf); + if (written < 0) + return -1; + if (buf != NULL) + buf += written; + written_total += written; + return written_total; + case JSONBoolean: + if (json_value_get_boolean(value)) + APPEND_STRING("true"); + else + APPEND_STRING("false"); + return written_total; + case JSONNumber: + num = json_value_get_number(value); + if (buf != NULL) + num_buf = buf; + if (num == ((double)(int)num)) /* check if num is integer */ + written = sprintf(num_buf, "%d", (int)num); + else + written = sprintf(num_buf, DOUBLE_SERIALIZATION_FORMAT, num); + if (written < 0) + return -1; + if (buf != NULL) + buf += written; + written_total += written; + return written_total; + case JSONNull: + APPEND_STRING("null"); + return written_total; + case JSONError: + return -1; + default: + return -1; + } +} + +static int json_serialize_string(const char *string, char *buf) { + size_t i = 0, len = strlen(string); + char c = '\0'; + int written = -1, written_total = 0; + APPEND_STRING("\""); + for (i = 0; i < len; i++) { + c = string[i]; + switch (c) { + case '\"': APPEND_STRING("\\\""); break; + case '\\': APPEND_STRING("\\\\"); break; + case '/': APPEND_STRING("\\/"); break; /* to make json embeddable in xml\/html */ + case '\b': APPEND_STRING("\\b"); break; + case '\f': APPEND_STRING("\\f"); break; + case '\n': APPEND_STRING("\\n"); break; + case '\r': APPEND_STRING("\\r"); break; + case '\t': APPEND_STRING("\\t"); break; + default: + if (buf != NULL) { + buf[0] = c; + buf += 1; + } + written_total += 1; + break; + } + } + APPEND_STRING("\""); + return written_total; +} + +static int append_indent(char *buf, int level) { + int i; + int written = -1, written_total = 0; + for (i = 0; i < level; i++) { + APPEND_STRING(" "); + } + return written_total; +} + +static int append_string(char *buf, const char *string) { + if (buf == NULL) { + return (int)strlen(string); + } + return sprintf(buf, "%s", string); +} + +#undef APPEND_STRING +#undef APPEND_INDENT + +/* Parser API */ +JSON_Value * json_parse_file(const char *filename) { + char *file_contents = read_file(filename); + JSON_Value *output_value = NULL; + if (file_contents == NULL) + return NULL; + output_value = json_parse_string(file_contents); + parson_free(file_contents); + return output_value; +} + +JSON_Value * json_parse_file_with_comments(const char *filename) { + char *file_contents = read_file(filename); + JSON_Value *output_value = NULL; + if (file_contents == NULL) + return NULL; + output_value = json_parse_string_with_comments(file_contents); + parson_free(file_contents); + return output_value; +} + +JSON_Value * json_parse_string(const char *string) { + if (string == NULL) + return NULL; + SKIP_WHITESPACES(&string); + if (*string != '{' && *string != '[') + return NULL; + return parse_value((const char**)&string, 0); +} + +JSON_Value * json_parse_string_with_comments(const char *string) { + JSON_Value *result = NULL; + char *string_mutable_copy = NULL, *string_mutable_copy_ptr = NULL; + string_mutable_copy = parson_strdup(string); + if (string_mutable_copy == NULL) + return NULL; + remove_comments(string_mutable_copy, "/*", "*/"); + remove_comments(string_mutable_copy, "//", "\n"); + string_mutable_copy_ptr = string_mutable_copy; + SKIP_WHITESPACES(&string_mutable_copy_ptr); + if (*string_mutable_copy_ptr != '{' && *string_mutable_copy_ptr != '[') { + parson_free(string_mutable_copy); + return NULL; + } + result = parse_value((const char**)&string_mutable_copy_ptr, 0); + parson_free(string_mutable_copy); + return result; +} + + +/* JSON Object API */ + +JSON_Value * json_object_get_value(const JSON_Object *object, const char *name) { + if (object == NULL || name == NULL) + return NULL; + return json_object_nget_value(object, name, strlen(name)); +} + +const char * json_object_get_string(const JSON_Object *object, const char *name) { + return json_value_get_string(json_object_get_value(object, name)); +} + +double json_object_get_number(const JSON_Object *object, const char *name) { + return json_value_get_number(json_object_get_value(object, name)); +} + +JSON_Object * json_object_get_object(const JSON_Object *object, const char *name) { + return json_value_get_object(json_object_get_value(object, name)); +} + +JSON_Array * json_object_get_array(const JSON_Object *object, const char *name) { + return json_value_get_array(json_object_get_value(object, name)); +} + +int json_object_get_boolean(const JSON_Object *object, const char *name) { + return json_value_get_boolean(json_object_get_value(object, name)); +} + +JSON_Value * json_object_dotget_value(const JSON_Object *object, const char *name) { + const char *dot_position = strchr(name, '.'); + if (!dot_position) + return json_object_get_value(object, name); + object = json_value_get_object(json_object_nget_value(object, name, dot_position - name)); + return json_object_dotget_value(object, dot_position + 1); +} + +const char * json_object_dotget_string(const JSON_Object *object, const char *name) { + return json_value_get_string(json_object_dotget_value(object, name)); +} + +double json_object_dotget_number(const JSON_Object *object, const char *name) { + return json_value_get_number(json_object_dotget_value(object, name)); +} + +JSON_Object * json_object_dotget_object(const JSON_Object *object, const char *name) { + return json_value_get_object(json_object_dotget_value(object, name)); +} + +JSON_Array * json_object_dotget_array(const JSON_Object *object, const char *name) { + return json_value_get_array(json_object_dotget_value(object, name)); +} + +int json_object_dotget_boolean(const JSON_Object *object, const char *name) { + return json_value_get_boolean(json_object_dotget_value(object, name)); +} + +size_t json_object_get_count(const JSON_Object *object) { + return object ? object->count : 0; +} + +const char * json_object_get_name(const JSON_Object *object, size_t index) { + if (index >= json_object_get_count(object)) + return NULL; + return object->names[index]; +} + +/* JSON Array API */ +JSON_Value * json_array_get_value(const JSON_Array *array, size_t index) { + if (index >= json_array_get_count(array)) + return NULL; + return array->items[index]; +} + +const char * json_array_get_string(const JSON_Array *array, size_t index) { + return json_value_get_string(json_array_get_value(array, index)); +} + +double json_array_get_number(const JSON_Array *array, size_t index) { + return json_value_get_number(json_array_get_value(array, index)); +} + +JSON_Object * json_array_get_object(const JSON_Array *array, size_t index) { + return json_value_get_object(json_array_get_value(array, index)); +} + +JSON_Array * json_array_get_array(const JSON_Array *array, size_t index) { + return json_value_get_array(json_array_get_value(array, index)); +} + +int json_array_get_boolean(const JSON_Array *array, size_t index) { + return json_value_get_boolean(json_array_get_value(array, index)); +} + +size_t json_array_get_count(const JSON_Array *array) { + return array ? array->count : 0; +} + +/* JSON Value API */ +JSON_Value_Type json_value_get_type(const JSON_Value *value) { + return value ? value->type : JSONError; +} + +JSON_Object * json_value_get_object(const JSON_Value *value) { + return json_value_get_type(value) == JSONObject ? value->value.object : NULL; +} + +JSON_Array * json_value_get_array(const JSON_Value *value) { + return json_value_get_type(value) == JSONArray ? value->value.array : NULL; +} + +const char * json_value_get_string(const JSON_Value *value) { + return json_value_get_type(value) == JSONString ? value->value.string : NULL; +} + +double json_value_get_number(const JSON_Value *value) { + return json_value_get_type(value) == JSONNumber ? value->value.number : 0; +} + +int json_value_get_boolean(const JSON_Value *value) { + return json_value_get_type(value) == JSONBoolean ? value->value.boolean : -1; +} + +void json_value_free(JSON_Value *value) { + switch (json_value_get_type(value)) { + case JSONObject: + json_object_free(value->value.object); + break; + case JSONString: + if (value->value.string) { parson_free(value->value.string); } + break; + case JSONArray: + json_array_free(value->value.array); + break; + default: + break; + } + parson_free(value); +} + +JSON_Value * json_value_init_object(void) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) + return NULL; + new_value->type = JSONObject; + new_value->value.object = json_object_init(); + if (!new_value->value.object) { + parson_free(new_value); + return NULL; + } + return new_value; +} + +JSON_Value * json_value_init_array(void) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) + return NULL; + new_value->type = JSONArray; + new_value->value.array = json_array_init(); + if (!new_value->value.array) { + parson_free(new_value); + return NULL; + } + return new_value; +} + +JSON_Value * json_value_init_string(const char *string) { + char *copy = NULL; + JSON_Value *value; + size_t string_len = 0; + if (string == NULL) + return NULL; + string_len = strlen(string); + if (!is_valid_utf8(string, string_len)) + return NULL; + copy = parson_strndup(string, string_len); + if (copy == NULL) + return NULL; + value = json_value_init_string_no_copy(copy); + if (value == NULL) + parson_free(copy); + return value; +} + +JSON_Value * json_value_init_number(double number) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) + return NULL; + new_value->type = JSONNumber; + new_value->value.number = number; + return new_value; +} + +JSON_Value * json_value_init_boolean(int boolean) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) + return NULL; + new_value->type = JSONBoolean; + new_value->value.boolean = boolean ? 1 : 0; + return new_value; +} + +JSON_Value * json_value_init_null(void) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) + return NULL; + new_value->type = JSONNull; + return new_value; +} + +JSON_Value * json_value_deep_copy(const JSON_Value *value) { + size_t i = 0; + JSON_Value *return_value = NULL, *temp_value_copy = NULL, *temp_value = NULL; + const char *temp_string = NULL, *temp_key = NULL; + char *temp_string_copy = NULL; + JSON_Array *temp_array = NULL, *temp_array_copy = NULL; + JSON_Object *temp_object = NULL, *temp_object_copy = NULL; + + switch (json_value_get_type(value)) { + case JSONArray: + temp_array = json_value_get_array(value); + return_value = json_value_init_array(); + if (return_value == NULL) + return NULL; + temp_array_copy = json_value_get_array(return_value); + for (i = 0; i < json_array_get_count(temp_array); i++) { + temp_value = json_array_get_value(temp_array, i); + temp_value_copy = json_value_deep_copy(temp_value); + if (temp_value_copy == NULL) { + json_value_free(return_value); + return NULL; + } + if (json_array_add(temp_array_copy, temp_value_copy) == JSONFailure) { + json_value_free(return_value); + json_value_free(temp_value_copy); + return NULL; + } + } + return return_value; + case JSONObject: + temp_object = json_value_get_object(value); + return_value = json_value_init_object(); + if (return_value == NULL) + return NULL; + temp_object_copy = json_value_get_object(return_value); + for (i = 0; i < json_object_get_count(temp_object); i++) { + temp_key = json_object_get_name(temp_object, i); + temp_value = json_object_get_value(temp_object, temp_key); + temp_value_copy = json_value_deep_copy(temp_value); + if (temp_value_copy == NULL) { + json_value_free(return_value); + return NULL; + } + if (json_object_add(temp_object_copy, temp_key, temp_value_copy) == JSONFailure) { + json_value_free(return_value); + json_value_free(temp_value_copy); + return NULL; + } + } + return return_value; + case JSONBoolean: + return json_value_init_boolean(json_value_get_boolean(value)); + case JSONNumber: + return json_value_init_number(json_value_get_number(value)); + case JSONString: + temp_string = json_value_get_string(value); + temp_string_copy = parson_strdup(temp_string); + if (temp_string_copy == NULL) + return NULL; + return_value = json_value_init_string_no_copy(temp_string_copy); + if (return_value == NULL) + parson_free(temp_string_copy); + return return_value; + case JSONNull: + return json_value_init_null(); + case JSONError: + return NULL; + default: + return NULL; + } +} + +size_t json_serialization_size(const JSON_Value *value) { + char num_buf[1100]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */ + int res = json_serialize_to_buffer_r(value, NULL, 0, 0, num_buf); + return res < 0 ? 0 : (size_t)(res + 1); +} + +JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes) { + int written = -1; + size_t needed_size_in_bytes = json_serialization_size(value); + if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) { + return JSONFailure; + } + written = json_serialize_to_buffer_r(value, buf, 0, 0, NULL); + if (written < 0) + return JSONFailure; + return JSONSuccess; +} + +JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename) { + JSON_Status return_code = JSONSuccess; + FILE *fp = NULL; + char *serialized_string = json_serialize_to_string(value); + if (serialized_string == NULL) { + return JSONFailure; + } + fp = fopen (filename, "w"); + if (fp != NULL) { + if (fputs (serialized_string, fp) == EOF) { + return_code = JSONFailure; + } + if (fclose (fp) == EOF) { + return_code = JSONFailure; + } + } + json_free_serialized_string(serialized_string); + return return_code; +} + +char * json_serialize_to_string(const JSON_Value *value) { + JSON_Status serialization_result = JSONFailure; + size_t buf_size_bytes = json_serialization_size(value); + char *buf = NULL; + if (buf_size_bytes == 0) { + return NULL; + } + buf = (char*)parson_malloc(buf_size_bytes); + if (buf == NULL) + return NULL; + serialization_result = json_serialize_to_buffer(value, buf, buf_size_bytes); + if (serialization_result == JSONFailure) { + json_free_serialized_string(buf); + return NULL; + } + return buf; +} + +size_t json_serialization_size_pretty(const JSON_Value *value) { + char num_buf[1100]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */ + int res = json_serialize_to_buffer_r(value, NULL, 0, 1, num_buf); + return res < 0 ? 0 : (size_t)(res + 1); +} + +JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes) { + int written = -1; + size_t needed_size_in_bytes = json_serialization_size_pretty(value); + if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) + return JSONFailure; + written = json_serialize_to_buffer_r(value, buf, 0, 1, NULL); + if (written < 0) + return JSONFailure; + return JSONSuccess; +} + +JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename) { + JSON_Status return_code = JSONSuccess; + FILE *fp = NULL; + char *serialized_string = json_serialize_to_string_pretty(value); + if (serialized_string == NULL) { + return JSONFailure; + } + fp = fopen (filename, "w"); + if (fp != NULL) { + if (fputs (serialized_string, fp) == EOF) { + return_code = JSONFailure; + } + if (fclose (fp) == EOF) { + return_code = JSONFailure; + } + } + json_free_serialized_string(serialized_string); + return return_code; +} + +char * json_serialize_to_string_pretty(const JSON_Value *value) { + JSON_Status serialization_result = JSONFailure; + size_t buf_size_bytes = json_serialization_size_pretty(value); + char *buf = NULL; + if (buf_size_bytes == 0) { + return NULL; + } + buf = (char*)parson_malloc(buf_size_bytes); + if (buf == NULL) + return NULL; + serialization_result = json_serialize_to_buffer_pretty(value, buf, buf_size_bytes); + if (serialization_result == JSONFailure) { + json_free_serialized_string(buf); + return NULL; + } + return buf; +} + +void json_free_serialized_string(char *string) { + parson_free(string); +} + +JSON_Status json_array_remove(JSON_Array *array, size_t ix) { + JSON_Value *temp_value = NULL; + size_t last_element_ix = 0; + if (array == NULL || ix >= json_array_get_count(array)) { + return JSONFailure; + } + last_element_ix = json_array_get_count(array) - 1; + json_value_free(json_array_get_value(array, ix)); + if (ix != last_element_ix) { /* Replace value with one from the end of array */ + temp_value = json_array_get_value(array, last_element_ix); + if (temp_value == NULL) { + return JSONFailure; + } + array->items[ix] = temp_value; + } + array->count -= 1; + return JSONSuccess; +} + +JSON_Status json_array_replace_value(JSON_Array *array, size_t ix, JSON_Value *value) { + if (array == NULL || value == NULL || ix >= json_array_get_count(array)) { + return JSONFailure; + } + json_value_free(json_array_get_value(array, ix)); + array->items[ix] = value; + return JSONSuccess; +} + +JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string) { + JSON_Value *value = json_value_init_string(string); + if (value == NULL) + return JSONFailure; + if (json_array_replace_value(array, i, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number) { + JSON_Value *value = json_value_init_number(number); + if (value == NULL) + return JSONFailure; + if (json_array_replace_value(array, i, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean) { + JSON_Value *value = json_value_init_boolean(boolean); + if (value == NULL) + return JSONFailure; + if (json_array_replace_value(array, i, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_replace_null(JSON_Array *array, size_t i) { + JSON_Value *value = json_value_init_null(); + if (value == NULL) + return JSONFailure; + if (json_array_replace_value(array, i, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_clear(JSON_Array *array) { + size_t i = 0; + if (array == NULL) + return JSONFailure; + for (i = 0; i < json_array_get_count(array); i++) { + json_value_free(json_array_get_value(array, i)); + } + array->count = 0; + return JSONSuccess; +} + +JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value) { + if (array == NULL || value == NULL) + return JSONFailure; + return json_array_add(array, value); +} + +JSON_Status json_array_append_string(JSON_Array *array, const char *string) { + JSON_Value *value = json_value_init_string(string); + if (value == NULL) + return JSONFailure; + if (json_array_append_value(array, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_append_number(JSON_Array *array, double number) { + JSON_Value *value = json_value_init_number(number); + if (value == NULL) + return JSONFailure; + if (json_array_append_value(array, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_append_boolean(JSON_Array *array, int boolean) { + JSON_Value *value = json_value_init_boolean(boolean); + if (value == NULL) + return JSONFailure; + if (json_array_append_value(array, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_append_null(JSON_Array *array) { + JSON_Value *value = json_value_init_null(); + if (value == NULL) + return JSONFailure; + if (json_array_append_value(array, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value) { + size_t i = 0; + JSON_Value *old_value; + if (object == NULL || name == NULL || value == NULL) + return JSONFailure; + old_value = json_object_get_value(object, name); + if (old_value != NULL) { /* free and overwrite old value */ + json_value_free(old_value); + for (i = 0; i < json_object_get_count(object); i++) { + if (strcmp(object->names[i], name) == 0) { + object->values[i] = value; + return JSONSuccess; + } + } + } + /* add new key value pair */ + return json_object_add(object, name, value); +} + +JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string) { + return json_object_set_value(object, name, json_value_init_string(string)); +} + +JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number) { + return json_object_set_value(object, name, json_value_init_number(number)); +} + +JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean) { + return json_object_set_value(object, name, json_value_init_boolean(boolean)); +} + +JSON_Status json_object_set_null(JSON_Object *object, const char *name) { + return json_object_set_value(object, name, json_value_init_null()); +} + +JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value) { + const char *dot_pos = NULL; + char *current_name = NULL; + JSON_Object *temp_obj = NULL; + JSON_Value *new_value = NULL; + if (value == NULL || name == NULL || value == NULL) + return JSONFailure; + dot_pos = strchr(name, '.'); + if (dot_pos == NULL) { + return json_object_set_value(object, name, value); + } else { + current_name = parson_strndup(name, dot_pos - name); + temp_obj = json_object_get_object(object, current_name); + if (temp_obj == NULL) { + new_value = json_value_init_object(); + if (new_value == NULL) { + parson_free(current_name); + return JSONFailure; + } + if (json_object_add(object, current_name, new_value) == JSONFailure) { + json_value_free(new_value); + parson_free(current_name); + return JSONFailure; + } + temp_obj = json_object_get_object(object, current_name); + } + parson_free(current_name); + return json_object_dotset_value(temp_obj, dot_pos + 1, value); + } +} + +JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string) { + JSON_Value *value = json_value_init_string(string); + if (value == NULL) + return JSONFailure; + if (json_object_dotset_value(object, name, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number) { + JSON_Value *value = json_value_init_number(number); + if (value == NULL) + return JSONFailure; + if (json_object_dotset_value(object, name, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean) { + JSON_Value *value = json_value_init_boolean(boolean); + if (value == NULL) + return JSONFailure; + if (json_object_dotset_value(object, name, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_object_dotset_null(JSON_Object *object, const char *name) { + JSON_Value *value = json_value_init_null(); + if (value == NULL) + return JSONFailure; + if (json_object_dotset_value(object, name, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_object_remove(JSON_Object *object, const char *name) { + size_t i = 0, last_item_index = 0; + if (object == NULL || json_object_get_value(object, name) == NULL) + return JSONFailure; + last_item_index = json_object_get_count(object) - 1; + for (i = 0; i < json_object_get_count(object); i++) { + if (strcmp(object->names[i], name) == 0) { + parson_free(object->names[i]); + json_value_free(object->values[i]); + if (i != last_item_index) { /* Replace key value pair with one from the end */ + object->names[i] = object->names[last_item_index]; + object->values[i] = object->values[last_item_index]; + } + object->count -= 1; + return JSONSuccess; + } + } + return JSONFailure; /* No execution path should end here */ +} + +JSON_Status json_object_dotremove(JSON_Object *object, const char *name) { + const char *dot_pos = strchr(name, '.'); + char *current_name = NULL; + JSON_Object *temp_obj = NULL; + if (dot_pos == NULL) { + return json_object_remove(object, name); + } else { + current_name = parson_strndup(name, dot_pos - name); + temp_obj = json_object_get_object(object, current_name); + if (temp_obj == NULL) { + parson_free(current_name); + return JSONFailure; + } + parson_free(current_name); + return json_object_dotremove(temp_obj, dot_pos + 1); + } +} + +JSON_Status json_object_clear(JSON_Object *object) { + size_t i = 0; + if (object == NULL) { + return JSONFailure; + } + for (i = 0; i < json_object_get_count(object); i++) { + parson_free(object->names[i]); + json_value_free(object->values[i]); + } + object->count = 0; + return JSONSuccess; +} + +JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value) { + JSON_Value *temp_schema_value = NULL, *temp_value = NULL; + JSON_Array *schema_array = NULL, *value_array = NULL; + JSON_Object *schema_object = NULL, *value_object = NULL; + JSON_Value_Type schema_type = JSONError, value_type = JSONError; + const char *key = NULL; + size_t i = 0, count = 0; + if (schema == NULL || value == NULL) + return JSONFailure; + schema_type = json_value_get_type(schema); + value_type = json_value_get_type(value); + if (schema_type != value_type && schema_type != JSONNull) /* null represents all values */ + return JSONFailure; + switch (schema_type) { + case JSONArray: + schema_array = json_value_get_array(schema); + value_array = json_value_get_array(value); + count = json_array_get_count(schema_array); + if (count == 0) + return JSONSuccess; /* Empty array allows all types */ + /* Get first value from array, rest is ignored */ + temp_schema_value = json_array_get_value(schema_array, 0); + for (i = 0; i < json_array_get_count(value_array); i++) { + temp_value = json_array_get_value(value_array, i); + if (json_validate(temp_schema_value, temp_value) == 0) { + return JSONFailure; + } + } + return JSONSuccess; + case JSONObject: + schema_object = json_value_get_object(schema); + value_object = json_value_get_object(value); + count = json_object_get_count(schema_object); + if (count == 0) + return JSONSuccess; /* Empty object allows all objects */ + else if (json_object_get_count(value_object) < count) + return JSONFailure; /* Tested object mustn't have less name-value pairs than schema */ + for (i = 0; i < count; i++) { + key = json_object_get_name(schema_object, i); + temp_schema_value = json_object_get_value(schema_object, key); + temp_value = json_object_get_value(value_object, key); + if (temp_value == NULL) + return JSONFailure; + if (json_validate(temp_schema_value, temp_value) == JSONFailure) + return JSONFailure; + } + return JSONSuccess; + case JSONString: case JSONNumber: case JSONBoolean: case JSONNull: + return JSONSuccess; /* equality already tested before switch */ + case JSONError: default: + return JSONFailure; + } +} + +JSON_Status json_value_equals(const JSON_Value *a, const JSON_Value *b) { + JSON_Object *a_object = NULL, *b_object = NULL; + JSON_Array *a_array = NULL, *b_array = NULL; + const char *a_string = NULL, *b_string = NULL; + const char *key = NULL; + size_t a_count = 0, b_count = 0, i = 0; + JSON_Value_Type a_type, b_type; + a_type = json_value_get_type(a); + b_type = json_value_get_type(b); + if (a_type != b_type) { + return 0; + } + switch (a_type) { + case JSONArray: + a_array = json_value_get_array(a); + b_array = json_value_get_array(b); + a_count = json_array_get_count(a_array); + b_count = json_array_get_count(b_array); + if (a_count != b_count) { + return 0; + } + for (i = 0; i < a_count; i++) { + if (!json_value_equals(json_array_get_value(a_array, i), + json_array_get_value(b_array, i))) { + return 0; + } + } + return 1; + case JSONObject: + a_object = json_value_get_object(a); + b_object = json_value_get_object(b); + a_count = json_object_get_count(a_object); + b_count = json_object_get_count(b_object); + if (a_count != b_count) { + return 0; + } + for (i = 0; i < a_count; i++) { + key = json_object_get_name(a_object, i); + if (!json_value_equals(json_object_get_value(a_object, key), + json_object_get_value(b_object, key))) { + return 0; + } + } + return 1; + case JSONString: + a_string = json_value_get_string(a); + b_string = json_value_get_string(b); + return strcmp(a_string, b_string) == 0; + case JSONBoolean: + return json_value_get_boolean(a) == json_value_get_boolean(b); + case JSONNumber: + return fabs(json_value_get_number(a) - json_value_get_number(b)) < 0.000001; /* EPSILON */ + case JSONError: + return 1; + case JSONNull: + return 1; + default: + return 1; + } +} + +JSON_Value_Type json_type(const JSON_Value *value) { + return json_value_get_type(value); +} + +JSON_Object * json_object (const JSON_Value *value) { + return json_value_get_object(value); +} + +JSON_Array * json_array (const JSON_Value *value) { + return json_value_get_array(value); +} + +const char * json_string (const JSON_Value *value) { + return json_value_get_string(value); +} + +double json_number (const JSON_Value *value) { + return json_value_get_number(value); +} + +int json_boolean(const JSON_Value *value) { + return json_value_get_boolean(value); +} + +void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun) { + parson_malloc = malloc_fun; + parson_free = free_fun; +} diff --git a/util_pkt_logger/src/util_pkt_logger.c b/util_pkt_logger/src/util_pkt_logger.c new file mode 100644 index 0000000..38a50f7 --- /dev/null +++ b/util_pkt_logger/src/util_pkt_logger.c @@ -0,0 +1,626 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Configure LoRa concentrator and record received packets in a log file + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +/* fix an issue between POSIX and C99 */ +#if __STDC_VERSION__ >= 199901L + #define _XOPEN_SOURCE 600 +#else + #define _XOPEN_SOURCE 500 +#endif + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ +#include <stdio.h> /* printf fprintf sprintf fopen fputs */ + +#include <string.h> /* memset */ +#include <signal.h> /* sigaction */ +#include <time.h> /* time clock_gettime strftime gmtime clock_nanosleep*/ +#include <unistd.h> /* getopt access */ +#include <stdlib.h> /* atoi */ + +#include "parson.h" +#include "loragw_hal.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#define MSG(args...) fprintf(stderr,"loragw_pkt_logger: " args) /* message that is destined to the user */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE VARIABLES (GLOBAL) ------------------------------------------- */ + +/* signal handling variables */ +struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */ +static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */ +static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */ + +/* configuration variables needed by the application */ +uint64_t lgwm = 0; /* LoRa gateway MAC address */ +char lgwm_str[17]; + +/* clock and log file management */ +time_t now_time; +time_t log_start_time; +FILE * log_file = NULL; +char log_file_name[64]; + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ + +static void sig_handler(int sigio); + +int parse_SX1301_configuration(const char * conf_file); + +int parse_gateway_configuration(const char * conf_file); + +void open_log(void); + +void usage (void); + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ + +static void sig_handler(int sigio) { + if (sigio == SIGQUIT) { + quit_sig = 1;; + } else if ((sigio == SIGINT) || (sigio == SIGTERM)) { + exit_sig = 1; + } +} + +int parse_SX1301_configuration(const char * conf_file) { + int i; + const char conf_obj[] = "SX1301_conf"; + char param_name[32]; /* used to generate variable parameter names */ + const char *str; /* used to store string value from JSON object */ + struct lgw_conf_board_s boardconf; + struct lgw_conf_rxrf_s rfconf; + struct lgw_conf_rxif_s ifconf; + JSON_Value *root_val; + JSON_Object *root = NULL; + JSON_Object *conf = NULL; + JSON_Value *val; + uint32_t sf, bw; + + /* try to parse JSON */ + root_val = json_parse_file_with_comments(conf_file); + root = json_value_get_object(root_val); + if (root == NULL) { + MSG("ERROR: %s id not a valid JSON file\n", conf_file); + exit(EXIT_FAILURE); + } + conf = json_object_get_object(root, conf_obj); + if (conf == NULL) { + MSG("INFO: %s does not contain a JSON object named %s\n", conf_file, conf_obj); + return -1; + } else { + MSG("INFO: %s does contain a JSON object named %s, parsing SX1301 parameters\n", conf_file, conf_obj); + } + + /* set board configuration */ + memset(&boardconf, 0, sizeof boardconf); /* initialize configuration structure */ + val = json_object_get_value(conf, "lorawan_public"); /* fetch value (if possible) */ + if (json_value_get_type(val) == JSONBoolean) { + boardconf.lorawan_public = (bool)json_value_get_boolean(val); + } else { + MSG("WARNING: Data type for lorawan_public seems wrong, please check\n"); + boardconf.lorawan_public = false; + } + val = json_object_get_value(conf, "clksrc"); /* fetch value (if possible) */ + if (json_value_get_type(val) == JSONNumber) { + boardconf.clksrc = (uint8_t)json_value_get_number(val); + } else { + MSG("WARNING: Data type for clksrc seems wrong, please check\n"); + boardconf.clksrc = 0; + } + MSG("INFO: lorawan_public %d, clksrc %d\n", boardconf.lorawan_public, boardconf.clksrc); + /* all parameters parsed, submitting configuration to the HAL */ + if (lgw_board_setconf(boardconf) != LGW_HAL_SUCCESS) { + MSG("ERROR: Failed to configure board\n"); + return -1; + } + + /* set configuration for RF chains */ + for (i = 0; i < LGW_RF_CHAIN_NB; ++i) { + memset(&rfconf, 0, sizeof(rfconf)); /* initialize configuration structure */ + sprintf(param_name, "radio_%i", i); /* compose parameter path inside JSON structure */ + val = json_object_get_value(conf, param_name); /* fetch value (if possible) */ + if (json_value_get_type(val) != JSONObject) { + MSG("INFO: no configuration for radio %i\n", i); + continue; + } + /* there is an object to configure that radio, let's parse it */ + sprintf(param_name, "radio_%i.enable", i); + val = json_object_dotget_value(conf, param_name); + if (json_value_get_type(val) == JSONBoolean) { + rfconf.enable = (bool)json_value_get_boolean(val); + } else { + rfconf.enable = false; + } + if (rfconf.enable == false) { /* radio disabled, nothing else to parse */ + MSG("INFO: radio %i disabled\n", i); + } else { /* radio enabled, will parse the other parameters */ + snprintf(param_name, sizeof param_name, "radio_%i.freq", i); + rfconf.freq_hz = (uint32_t)json_object_dotget_number(conf, param_name); + snprintf(param_name, sizeof param_name, "radio_%i.rssi_offset", i); + rfconf.rssi_offset = (float)json_object_dotget_number(conf, param_name); + snprintf(param_name, sizeof param_name, "radio_%i.type", i); + str = json_object_dotget_string(conf, param_name); + if (!strncmp(str, "SX1255", 6)) { + rfconf.type = LGW_RADIO_TYPE_SX1255; + } else if (!strncmp(str, "SX1257", 6)) { + rfconf.type = LGW_RADIO_TYPE_SX1257; + } else { + MSG("WARNING: invalid radio type: %s (should be SX1255 or SX1257)\n", str); + } + snprintf(param_name, sizeof param_name, "radio_%i.tx_enable", i); + val = json_object_dotget_value(conf, param_name); + if (json_value_get_type(val) == JSONBoolean) { + rfconf.tx_enable = (bool)json_value_get_boolean(val); + if (rfconf.tx_enable == true) { + /* tx notch filter frequency to be set */ + snprintf(param_name, sizeof param_name, "radio_%i.tx_notch_freq", i); + rfconf.tx_notch_freq = (uint32_t)json_object_dotget_number(conf, param_name); + } + } else { + rfconf.tx_enable = false; + } + MSG("INFO: radio %i enabled (type %s), center frequency %u, RSSI offset %f, tx enabled %d, tx_notch_freq %u\n", i, str, rfconf.freq_hz, rfconf.rssi_offset, rfconf.tx_enable, rfconf.tx_notch_freq); + } + /* all parameters parsed, submitting configuration to the HAL */ + if (lgw_rxrf_setconf(i, rfconf) != LGW_HAL_SUCCESS) { + MSG("ERROR: invalid configuration for radio %i\n", i); + return -1; + } + } + + /* set configuration for LoRa multi-SF channels (bandwidth cannot be set) */ + for (i = 0; i < LGW_MULTI_NB; ++i) { + memset(&ifconf, 0, sizeof(ifconf)); /* initialize configuration structure */ + sprintf(param_name, "chan_multiSF_%i", i); /* compose parameter path inside JSON structure */ + val = json_object_get_value(conf, param_name); /* fetch value (if possible) */ + if (json_value_get_type(val) != JSONObject) { + MSG("INFO: no configuration for LoRa multi-SF channel %i\n", i); + continue; + } + /* there is an object to configure that LoRa multi-SF channel, let's parse it */ + sprintf(param_name, "chan_multiSF_%i.enable", i); + val = json_object_dotget_value(conf, param_name); + if (json_value_get_type(val) == JSONBoolean) { + ifconf.enable = (bool)json_value_get_boolean(val); + } else { + ifconf.enable = false; + } + if (ifconf.enable == false) { /* LoRa multi-SF channel disabled, nothing else to parse */ + MSG("INFO: LoRa multi-SF channel %i disabled\n", i); + } else { /* LoRa multi-SF channel enabled, will parse the other parameters */ + sprintf(param_name, "chan_multiSF_%i.radio", i); + ifconf.rf_chain = (uint32_t)json_object_dotget_number(conf, param_name); + sprintf(param_name, "chan_multiSF_%i.if", i); + ifconf.freq_hz = (int32_t)json_object_dotget_number(conf, param_name); + // TODO: handle individual SF enabling and disabling (spread_factor) + MSG("INFO: LoRa multi-SF channel %i enabled, radio %i selected, IF %i Hz, 125 kHz bandwidth, SF 7 to 12\n", i, ifconf.rf_chain, ifconf.freq_hz); + } + /* all parameters parsed, submitting configuration to the HAL */ + if (lgw_rxif_setconf(i, ifconf) != LGW_HAL_SUCCESS) { + MSG("ERROR: invalid configuration for Lora multi-SF channel %i\n", i); + return -1; + } + } + + /* set configuration for LoRa standard channel */ + memset(&ifconf, 0, sizeof(ifconf)); /* initialize configuration structure */ + val = json_object_get_value(conf, "chan_Lora_std"); /* fetch value (if possible) */ + if (json_value_get_type(val) != JSONObject) { + MSG("INFO: no configuration for LoRa standard channel\n"); + } else { + val = json_object_dotget_value(conf, "chan_Lora_std.enable"); + if (json_value_get_type(val) == JSONBoolean) { + ifconf.enable = (bool)json_value_get_boolean(val); + } else { + ifconf.enable = false; + } + if (ifconf.enable == false) { + MSG("INFO: LoRa standard channel %i disabled\n", i); + } else { + ifconf.rf_chain = (uint32_t)json_object_dotget_number(conf, "chan_Lora_std.radio"); + ifconf.freq_hz = (int32_t)json_object_dotget_number(conf, "chan_Lora_std.if"); + bw = (uint32_t)json_object_dotget_number(conf, "chan_Lora_std.bandwidth"); + switch(bw) { + case 500000: ifconf.bandwidth = BW_500KHZ; break; + case 250000: ifconf.bandwidth = BW_250KHZ; break; + case 125000: ifconf.bandwidth = BW_125KHZ; break; + default: ifconf.bandwidth = BW_UNDEFINED; + } + sf = (uint32_t)json_object_dotget_number(conf, "chan_Lora_std.spread_factor"); + switch(sf) { + case 7: ifconf.datarate = DR_LORA_SF7; break; + case 8: ifconf.datarate = DR_LORA_SF8; break; + case 9: ifconf.datarate = DR_LORA_SF9; break; + case 10: ifconf.datarate = DR_LORA_SF10; break; + case 11: ifconf.datarate = DR_LORA_SF11; break; + case 12: ifconf.datarate = DR_LORA_SF12; break; + default: ifconf.datarate = DR_UNDEFINED; + } + MSG("INFO: LoRa standard channel enabled, radio %i selected, IF %i Hz, %u Hz bandwidth, SF %u\n", ifconf.rf_chain, ifconf.freq_hz, bw, sf); + } + if (lgw_rxif_setconf(8, ifconf) != LGW_HAL_SUCCESS) { + MSG("ERROR: invalid configuration for Lora standard channel\n"); + return -1; + } + } + + /* set configuration for FSK channel */ + memset(&ifconf, 0, sizeof(ifconf)); /* initialize configuration structure */ + val = json_object_get_value(conf, "chan_FSK"); /* fetch value (if possible) */ + if (json_value_get_type(val) != JSONObject) { + MSG("INFO: no configuration for FSK channel\n"); + } else { + val = json_object_dotget_value(conf, "chan_FSK.enable"); + if (json_value_get_type(val) == JSONBoolean) { + ifconf.enable = (bool)json_value_get_boolean(val); + } else { + ifconf.enable = false; + } + if (ifconf.enable == false) { + MSG("INFO: FSK channel %i disabled\n", i); + } else { + ifconf.rf_chain = (uint32_t)json_object_dotget_number(conf, "chan_FSK.radio"); + ifconf.freq_hz = (int32_t)json_object_dotget_number(conf, "chan_FSK.if"); + bw = (uint32_t)json_object_dotget_number(conf, "chan_FSK.bandwidth"); + if (bw <= 7800) ifconf.bandwidth = BW_7K8HZ; + else if (bw <= 15600) ifconf.bandwidth = BW_15K6HZ; + else if (bw <= 31200) ifconf.bandwidth = BW_31K2HZ; + else if (bw <= 62500) ifconf.bandwidth = BW_62K5HZ; + else if (bw <= 125000) ifconf.bandwidth = BW_125KHZ; + else if (bw <= 250000) ifconf.bandwidth = BW_250KHZ; + else if (bw <= 500000) ifconf.bandwidth = BW_500KHZ; + else ifconf.bandwidth = BW_UNDEFINED; + ifconf.datarate = (uint32_t)json_object_dotget_number(conf, "chan_FSK.datarate"); + MSG("INFO: FSK channel enabled, radio %i selected, IF %i Hz, %u Hz bandwidth, %u bps datarate\n", ifconf.rf_chain, ifconf.freq_hz, bw, ifconf.datarate); + } + if (lgw_rxif_setconf(9, ifconf) != LGW_HAL_SUCCESS) { + MSG("ERROR: invalid configuration for FSK channel\n"); + return -1; + } + } + json_value_free(root_val); + return 0; +} + +int parse_gateway_configuration(const char * conf_file) { + const char conf_obj[] = "gateway_conf"; + JSON_Value *root_val; + JSON_Object *root = NULL; + JSON_Object *conf = NULL; + const char *str; /* pointer to sub-strings in the JSON data */ + unsigned long long ull = 0; + + /* try to parse JSON */ + root_val = json_parse_file_with_comments(conf_file); + root = json_value_get_object(root_val); + if (root == NULL) { + MSG("ERROR: %s id not a valid JSON file\n", conf_file); + exit(EXIT_FAILURE); + } + conf = json_object_get_object(root, conf_obj); + if (conf == NULL) { + MSG("INFO: %s does not contain a JSON object named %s\n", conf_file, conf_obj); + return -1; + } else { + MSG("INFO: %s does contain a JSON object named %s, parsing gateway parameters\n", conf_file, conf_obj); + } + + /* getting network parameters (only those necessary for the packet logger) */ + str = json_object_get_string(conf, "gateway_ID"); + if (str != NULL) { + sscanf(str, "%llx", &ull); + lgwm = ull; + MSG("INFO: gateway MAC address is configured to %016llX\n", ull); + } + + json_value_free(root_val); + return 0; +} + +void open_log(void) { + int i; + char iso_date[20]; + + strftime(iso_date,ARRAY_SIZE(iso_date),"%Y%m%dT%H%M%SZ",gmtime(&now_time)); /* format yyyymmddThhmmssZ */ + log_start_time = now_time; /* keep track of when the log was started, for log rotation */ + + sprintf(log_file_name, "pktlog_%s_%s.csv", lgwm_str, iso_date); + log_file = fopen(log_file_name, "a"); /* create log file, append if file already exist */ + if (log_file == NULL) { + MSG("ERROR: impossible to create log file %s\n", log_file_name); + exit(EXIT_FAILURE); + } + + i = fprintf(log_file, "\"gateway ID\",\"node MAC\",\"UTC timestamp\",\"us count\",\"frequency\",\"RF chain\",\"RX chain\",\"status\",\"size\",\"modulation\",\"bandwidth\",\"datarate\",\"coderate\",\"RSSI\",\"SNR\",\"payload\"\n"); + if (i < 0) { + MSG("ERROR: impossible to write to log file %s\n", log_file_name); + exit(EXIT_FAILURE); + } + + MSG("INFO: Now writing to log file %s\n", log_file_name); + return; +} + +/* describe command line options */ +void usage(void) { + printf("*** Library version information ***\n%s\n\n", lgw_version_info()); + printf( "Available options:\n"); + printf( " -h print this help\n"); + printf( " -r <int> rotate log file every N seconds (-1 disable log rotation)\n"); +} + +/* -------------------------------------------------------------------------- */ +/* --- MAIN FUNCTION -------------------------------------------------------- */ + +int main(int argc, char **argv) +{ + int i, j; /* loop and temporary variables */ + struct timespec sleep_time = {0, 3000000}; /* 3 ms */ + + /* clock and log rotation management */ + int log_rotate_interval = 3600; /* by default, rotation every hour */ + int time_check = 0; /* variable used to limit the number of calls to time() function */ + unsigned long pkt_in_log = 0; /* count the number of packet written in each log file */ + + /* configuration file related */ + const char global_conf_fname[] = "global_conf.json"; /* contain global (typ. network-wide) configuration */ + const char local_conf_fname[] = "local_conf.json"; /* contain node specific configuration, overwrite global parameters for parameters that are defined in both */ + const char debug_conf_fname[] = "debug_conf.json"; /* if present, all other configuration files are ignored */ + + /* allocate memory for packet fetching and processing */ + struct lgw_pkt_rx_s rxpkt[16]; /* array containing up to 16 inbound packets metadata */ + struct lgw_pkt_rx_s *p; /* pointer on a RX packet */ + int nb_pkt; + + /* local timestamp variables until we get accurate GPS time */ + struct timespec fetch_time; + char fetch_timestamp[30]; + struct tm * x; + + /* parse command line options */ + while ((i = getopt (argc, argv, "hr:")) != -1) { + switch (i) { + case 'h': + usage(); + return EXIT_FAILURE; + break; + + case 'r': + log_rotate_interval = atoi(optarg); + if ((log_rotate_interval == 0) || (log_rotate_interval < -1)) { + MSG( "ERROR: Invalid argument for -r option\n"); + return EXIT_FAILURE; + } + break; + + default: + MSG("ERROR: argument parsing use -h option for help\n"); + usage(); + return EXIT_FAILURE; + } + } + + /* configure signal handling */ + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_handler = sig_handler; + sigaction(SIGQUIT, &sigact, NULL); + sigaction(SIGINT, &sigact, NULL); + sigaction(SIGTERM, &sigact, NULL); + + /* configuration files management */ + if (access(debug_conf_fname, R_OK) == 0) { + /* if there is a debug conf, parse only the debug conf */ + MSG("INFO: found debug configuration file %s, other configuration files will be ignored\n", debug_conf_fname); + parse_SX1301_configuration(debug_conf_fname); + parse_gateway_configuration(debug_conf_fname); + } else if (access(global_conf_fname, R_OK) == 0) { + /* if there is a global conf, parse it and then try to parse local conf */ + MSG("INFO: found global configuration file %s, trying to parse it\n", global_conf_fname); + parse_SX1301_configuration(global_conf_fname); + parse_gateway_configuration(global_conf_fname); + if (access(local_conf_fname, R_OK) == 0) { + MSG("INFO: found local configuration file %s, trying to parse it\n", local_conf_fname); + parse_SX1301_configuration(local_conf_fname); + parse_gateway_configuration(local_conf_fname); + } + } else if (access(local_conf_fname, R_OK) == 0) { + /* if there is only a local conf, parse it and that's all */ + MSG("INFO: found local configuration file %s, trying to parse it\n", local_conf_fname); + parse_SX1301_configuration(local_conf_fname); + parse_gateway_configuration(local_conf_fname); + } else { + MSG("ERROR: failed to find any configuration file named %s, %s or %s\n", global_conf_fname, local_conf_fname, debug_conf_fname); + return EXIT_FAILURE; + } + + /* starting the concentrator */ + i = lgw_start(); + if (i == LGW_HAL_SUCCESS) { + MSG("INFO: concentrator started, packet can now be received\n"); + } else { + MSG("ERROR: failed to start the concentrator\n"); + return EXIT_FAILURE; + } + + /* transform the MAC address into a string */ + sprintf(lgwm_str, "%08X%08X", (uint32_t)(lgwm >> 32), (uint32_t)(lgwm & 0xFFFFFFFF)); + + /* opening log file and writing CSV header*/ + time(&now_time); + open_log(); + + /* main loop */ + while ((quit_sig != 1) && (exit_sig != 1)) { + /* fetch packets */ + nb_pkt = lgw_receive(ARRAY_SIZE(rxpkt), rxpkt); + if (nb_pkt == LGW_HAL_ERROR) { + MSG("ERROR: failed packet fetch, exiting\n"); + return EXIT_FAILURE; + } else if (nb_pkt == 0) { + clock_nanosleep(CLOCK_MONOTONIC, 0, &sleep_time, NULL); /* wait a short time if no packets */ + } else { + /* local timestamp generation until we get accurate GPS time */ + clock_gettime(CLOCK_REALTIME, &fetch_time); + x = gmtime(&(fetch_time.tv_sec)); + sprintf(fetch_timestamp,"%04i-%02i-%02i %02i:%02i:%02i.%03liZ",(x->tm_year)+1900,(x->tm_mon)+1,x->tm_mday,x->tm_hour,x->tm_min,x->tm_sec,(fetch_time.tv_nsec)/1000000); /* ISO 8601 format */ + } + + /* log packets */ + for (i=0; i < nb_pkt; ++i) { + p = &rxpkt[i]; + + /* writing gateway ID */ + fprintf(log_file, "\"%08X%08X\",", (uint32_t)(lgwm >> 32), (uint32_t)(lgwm & 0xFFFFFFFF)); + + /* writing node MAC address */ + fputs("\"\",", log_file); // TODO: need to parse payload + + /* writing UTC timestamp*/ + fprintf(log_file, "\"%s\",", fetch_timestamp); + // TODO: replace with GPS time when available + + /* writing internal clock */ + fprintf(log_file, "%10u,", p->count_us); + + /* writing RX frequency */ + fprintf(log_file, "%10u,", p->freq_hz); + + /* writing RF chain */ + fprintf(log_file, "%u,", p->rf_chain); + + /* writing RX modem/IF chain */ + fprintf(log_file, "%2d,", p->if_chain); + + /* writing status */ + switch(p->status) { + case STAT_CRC_OK: fputs("\"CRC_OK\" ,", log_file); break; + case STAT_CRC_BAD: fputs("\"CRC_BAD\",", log_file); break; + case STAT_NO_CRC: fputs("\"NO_CRC\" ,", log_file); break; + case STAT_UNDEFINED: fputs("\"UNDEF\" ,", log_file); break; + default: fputs("\"ERR\" ,", log_file); + } + + /* writing payload size */ + fprintf(log_file, "%3u,", p->size); + + /* writing modulation */ + switch(p->modulation) { + case MOD_LORA: fputs("\"LORA\",", log_file); break; + case MOD_FSK: fputs("\"FSK\" ,", log_file); break; + default: fputs("\"ERR\" ,", log_file); + } + + /* writing bandwidth */ + switch(p->bandwidth) { + case BW_500KHZ: fputs("500000,", log_file); break; + case BW_250KHZ: fputs("250000,", log_file); break; + case BW_125KHZ: fputs("125000,", log_file); break; + case BW_62K5HZ: fputs("62500 ,", log_file); break; + case BW_31K2HZ: fputs("31200 ,", log_file); break; + case BW_15K6HZ: fputs("15600 ,", log_file); break; + case BW_7K8HZ: fputs("7800 ,", log_file); break; + case BW_UNDEFINED: fputs("0 ,", log_file); break; + default: fputs("-1 ,", log_file); + } + + /* writing datarate */ + if (p->modulation == MOD_LORA) { + switch (p->datarate) { + case DR_LORA_SF7: fputs("\"SF7\" ,", log_file); break; + case DR_LORA_SF8: fputs("\"SF8\" ,", log_file); break; + case DR_LORA_SF9: fputs("\"SF9\" ,", log_file); break; + case DR_LORA_SF10: fputs("\"SF10\" ,", log_file); break; + case DR_LORA_SF11: fputs("\"SF11\" ,", log_file); break; + case DR_LORA_SF12: fputs("\"SF12\" ,", log_file); break; + default: fputs("\"ERR\" ,", log_file); + } + } else if (p->modulation == MOD_FSK) { + fprintf(log_file, "\"%6u\",", p->datarate); + } else { + fputs("\"ERR\" ,", log_file); + } + + /* writing coderate */ + switch (p->coderate) { + case CR_LORA_4_5: fputs("\"4/5\",", log_file); break; + case CR_LORA_4_6: fputs("\"2/3\",", log_file); break; + case CR_LORA_4_7: fputs("\"4/7\",", log_file); break; + case CR_LORA_4_8: fputs("\"1/2\",", log_file); break; + case CR_UNDEFINED: fputs("\"\" ,", log_file); break; + default: fputs("\"ERR\",", log_file); + } + + /* writing packet RSSI */ + fprintf(log_file, "%+.0f,", p->rssi); + + /* writing packet average SNR */ + fprintf(log_file, "%+5.1f,", p->snr); + + /* writing hex-encoded payload (bundled in 32-bit words) */ + fputs("\"", log_file); + for (j = 0; j < p->size; ++j) { + if ((j > 0) && (j%4 == 0)) fputs("-", log_file); + fprintf(log_file, "%02X", p->payload[j]); + } + + /* end of log file line */ + fputs("\"\n", log_file); + fflush(log_file); + ++pkt_in_log; + } + + /* check time and rotate log file if necessary */ + ++time_check; + if (time_check >= 8) { + time_check = 0; + time(&now_time); + if (difftime(now_time, log_start_time) > log_rotate_interval) { + fclose(log_file); + MSG("INFO: log file %s closed, %lu packet(s) recorded\n", log_file_name, pkt_in_log); + pkt_in_log = 0; + open_log(); + } + } + } + + if (exit_sig == 1) { + /* clean up before leaving */ + i = lgw_stop(); + if (i == LGW_HAL_SUCCESS) { + MSG("INFO: concentrator stopped successfully\n"); + } else { + MSG("WARNING: failed to stop concentrator successfully\n"); + } + fclose(log_file); + MSG("INFO: log file %s closed, %lu packet(s) recorded\n", log_file_name, pkt_in_log); + } + + MSG("INFO: Exiting packet logger program\n"); + return EXIT_SUCCESS; +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/util_spectral_scan/Makefile b/util_spectral_scan/Makefile new file mode 100644 index 0000000..35d625f --- /dev/null +++ b/util_spectral_scan/Makefile @@ -0,0 +1,63 @@ +### Environment constants + +LGW_PATH ?= ../libloragw +ARCH ?= +CROSS_COMPILE ?= + +### External constant definitions + +include $(LGW_PATH)/library.cfg + +### Constant symbols + +CC = $(CROSS_COMPILE)gcc +AR = $(CROSS_COMPILE)ar +CFLAGS = -O2 -Wall -Wextra -std=c99 -I inc + +OBJDIR = obj +INCLUDES = $(wildcard inc/*.h) + +### Constants for LoRa concentrator HAL library +# List the library sub-modules that are used by the application + +LGW_INC = $(LGW_PATH)/inc/config.h +LGW_INC += $(LGW_PATH)/inc/loragw_hal.h + +### Linking options + +LIBS := -lloragw -lrt + +### General build targets + +all: util_spectral_scan + +clean: + rm -f $(OBJDIR)/*.o + rm -f util_spectral_scan + +### HAL library (do no force multiple library rebuild even with 'make -B') + +$(LGW_PATH)/inc/config.h: + @if test ! -f $@; then \ + $(MAKE) all -C $(LGW_PATH); \ + fi + +$(LGW_PATH)/libloragw.a: $(LGW_INC) + @if test ! -f $@; then \ + $(MAKE) all -C $(LGW_PATH); \ + fi + +### Sub-modules compilation + +$(OBJDIR): + mkdir -p $(OBJDIR) + +$(OBJDIR)/%.o: src/%.c $(INCLUDES) | $(OBJDIR) + $(CC) -c $(CFLAGS) -I$(LGW_PATH)/inc $< -o $@ + +### Main program assembly + +util_spectral_scan: $(OBJDIR)/util_spectral_scan.o + $(CC) -L$(LGW_PATH) $^ $(LIBS) -o $@ + +### EOF diff --git a/util_spectral_scan/readme.md b/util_spectral_scan/readme.md new file mode 100644 index 0000000..6bcd33e --- /dev/null +++ b/util_spectral_scan/readme.md @@ -0,0 +1,79 @@ + ______ _ + / _____) _ | | + ( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | + (______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2014 Semtech-Cycleo + +Background Spectral Scan for LoRa gateway +========================================= + + +1. Introduction +---------------- + +This software is used to scan the spectral band where the LoRa gateway operates. +It simply computes a RSSI histogram on several frequencies, that will help to +detect occupied bands and get interferer profiles. +It logs the histogram in a .csv file. + +This utility program is meant to run on the LoRa gateway reference design +SX1301AP2 (with FPGA and additionnal SX127x). + +The background RSSI scan is a diagnostic tool and must be run on top of the +gateway activity. Moreover the two SX1257 radios have to be configured in RX +mode to optimize the matching impedance with SX127x. The 32MHz clock provided +to the SX127x is available once SX1301 has enabled the two SX1257 radios, so +the background RSSI scan must be launched after the packet forwarder. + +Note: if the FPGA running the spectral scan also supports Listen-Before-Talk +feature (LBT), the LBT feature has to be enabled and running before launching +util_spectral_scan. For example, if the lora_pkt_fwd runs in background, it has +to use a global_conf.json file with "lbt_cfg.enable" set to true. + +2. Command line options +------------------------ + +`-h` +will display a short help + +`-f start:step:stop` +Frequency vector to scan in MHz: start:step:stop +Valid range: start > 800, step > 0.005, stop < 1000 + +`-b` +Channel bandwidth to be used to configure the SX1272x radio for scanning in KHz +Valid values: [25,50,100,125,200,250,500] + +`-n` +Total number of RSSI points. +Valid range: [1..65535] + +`-l` +Log file name + +Note: For FPGA image that provides LBT support, the spectral scan gets less +flexible. The following parameters have constraints: + - Frequency step: has to be multiple of 100KHz + - Channel bandwidth: hardcoded to 200KHz + - Number of RSSI points: 16641 + +3. Usage +--------- + +The format of the log file is the following: +Freq_1, RSSI_1, histo_1, ...., RSSI_128, histo_128 +Freq_2, RSSI_1, histo_1, ...., RSSI_128, histo_128 +... + +RSSI_n is the nth value of RSSI in dBm + +Default setup: +- freq 863 : 0.2 : 870 +- 65535 RSSI points in total at 32kHz rate +- 125KHz channel bandwidth + +Example with frequencies from 865 MHz to 870 MHz, by step of 100KHz, BW 200KHz +10000 RSSI points processed at 10kHz rate, saved in "log.csv": +./util_spectral_scan -f 865:0.1:870 -n 10000 -b 200 -l "log" diff --git a/util_spectral_scan/src/util_spectral_scan.c b/util_spectral_scan/src/util_spectral_scan.c new file mode 100644 index 0000000..d2aecda --- /dev/null +++ b/util_spectral_scan/src/util_spectral_scan.c @@ -0,0 +1,403 @@ +/* + ______ _ + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2014 Semtech-Cycleo + +Description: + SX1301 spectral scan + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Michael Coracin +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDENCIES --------------------------------------------------------- */ + +/* Fix an issue between POSIX and C99 */ +#if __STDC_VERSION__ >= 199901L + #define _XOPEN_SOURCE 600 +#else + #define _XOPEN_SOURCE 500 +#endif + +#include <stdint.h> /* C99 types */ +#include <stdio.h> /* NULL printf */ +#include <stdlib.h> /* EXIT atoi */ +#include <unistd.h> /* getopt */ +#include <string.h> + +#include "loragw_aux.h" +#include "loragw_reg.h" +#include "loragw_hal.h" +#include "loragw_radio.h" +#include "loragw_fpga.h" + +/* -------------------------------------------------------------------------- */ +/* --- MACROS & CONSTANTS --------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +#define DEFAULT_START_FREQ 863000000 /* start frequency, Hz */ +#define DEFAULT_STOP_FREQ 870000000 /* stop frequency, Hz */ +#define DEFAULT_STEP_FREQ 200000 /* frequency step, Hz */ +#define DEFAULT_RSSI_PTS 65535 /* number of RSSI reads */ +#define DEFAULT_CHAN_BW LGW_SX127X_RXBW_62K5_HZ /* channel bandwidth */ +#define DEFAULT_LOG_NAME "rssi_histogram" +#define DEFAULT_SX127X_RSSI_OFFSET -4 + +#define RSSI_RANGE 256 + +#define MAX_FREQ 1000000000 +#define MIN_FREQ 800000000 +#define MIN_STEP_FREQ 5000 + +#define FPGA_FEATURE_SPECTRAL_SCAN 1 +#define FPGA_FEATURE_LBT 2 + +/* When FPGA supports LBT, there are few more constraints on above constants */ +#define LBT_DEFAULT_RSSI_PTS 129*129 /* number of RSSI reads, hard-coded in FPGA*/ +#define LBT_MIN_STEP_FREQ 100000 + +/* -------------------------------------------------------------------------- */ +/* --- GLOBAL VARIABLES ----------------------------------------------------- */ + +/* -------------------------------------------------------------------------- */ +/* --- MAIN FUNCTION -------------------------------------------------------- */ + +int main( int argc, char ** argv ) +{ + int i, j, k; /* loop and temporary variables */ + int x; /* return code for functions */ + int32_t reg_val; + + /* Parameter parsing */ + double arg_lf[3] = {0,0,0}; + unsigned arg_u = 0; + int arg_i = 0; + char arg_s[64]; + + /* Application parameters */ + uint32_t init_freq = DEFAULT_START_FREQ; + uint32_t start_freq = DEFAULT_START_FREQ; + uint32_t stop_freq = DEFAULT_STOP_FREQ; + uint32_t step_freq = DEFAULT_STEP_FREQ; + uint16_t rssi_pts = DEFAULT_RSSI_PTS; + int8_t rssi_offset = DEFAULT_SX127X_RSSI_OFFSET; + enum lgw_sx127x_rxbw_e channel_bw_khz = DEFAULT_CHAN_BW; + char log_file_name[64] = DEFAULT_LOG_NAME; + FILE * log_file = NULL; + + /* Local var */ + bool lbt_support = false; + int freq_idx; + int freq_nb; + uint64_t freq_reg; + uint32_t freq; + uint8_t read_burst[RSSI_RANGE*2]; + uint16_t rssi_histo; + uint16_t rssi_cumu; + float rssi_thresh[] = {0.1,0.3,0.5,0.8,1}; + + /* Parse command line options */ + while((i = getopt(argc, argv, "hf:n:b:l:o:")) != -1) { + switch (i) { + case 'h': + printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); + printf(" -f <float>:<float>:<float> Frequency vector to scan in MHz (start:step:stop)\n"); + printf(" start>%3.3f step>%1.3f stop<%3.3f\n", MIN_FREQ/1e6, MIN_STEP_FREQ/1e6, MAX_FREQ/1e6); + printf(" -b <uint> Channel bandwidth in KHz [25,50,100,125,200,250,500]\n"); + printf(" -n <uint> Total number of RSSI points [1..65535]\n"); + printf(" -o <int> Offset in dB to be applied to the SX127x RSSI [-128..127]\n"); + printf(" -l <char> Log file name\n"); + printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); + return EXIT_SUCCESS; + + case 'f': /* -f <float>:<float>:<float> Frequency vector to scan in MHz, start:step:stop */ + j = sscanf(optarg, "%lf:%lf:%lf", &arg_lf[0], &arg_lf[1], &arg_lf[2]); + if ((j!=3) || (arg_lf[0] < MIN_FREQ/1e6) || (arg_lf[0] > MAX_FREQ/1e6) || (arg_lf[1] < MIN_STEP_FREQ/1e6) || (arg_lf[2] < MIN_FREQ/1e6) || (arg_lf[2] > MAX_FREQ/1e6)) { + printf("ERROR: argument parsing of -f argument. -h for help.\n"); + return EXIT_FAILURE; + } else { + start_freq = (uint32_t)((arg_lf[0] * 1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */ + step_freq = (uint32_t)((arg_lf[1] * 1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */ + stop_freq = (uint32_t)((arg_lf[2] * 1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */ + } + break; + + case 'b': /* -b <uint> Channel bandwidth in KHz [25,50,100,125,200,250,500] */ + j = sscanf(optarg, "%u", &arg_u); + if (j != 1) { + printf("ERROR: argument parsing of -b argument. -h for help.\n"); + return EXIT_FAILURE; + } else { + switch (arg_u) { + case 25: + channel_bw_khz = LGW_SX127X_RXBW_12K5_HZ; + break; + case 50: + channel_bw_khz = LGW_SX127X_RXBW_25K_HZ; + break; + case 100: + channel_bw_khz = LGW_SX127X_RXBW_50K_HZ; + break; + case 125: + channel_bw_khz = LGW_SX127X_RXBW_62K5_HZ; + break; + case 200: + channel_bw_khz = LGW_SX127X_RXBW_100K_HZ; + break; + case 250: + channel_bw_khz = LGW_SX127X_RXBW_125K_HZ; + break; + case 500: + channel_bw_khz = LGW_SX127X_RXBW_250K_HZ; + break; + default: + printf("ERROR: argument parsing of -b argument. -h for help.\n"); + return EXIT_FAILURE; + } + } + break; + + case 'n': /* -n <uint> Total number of RSSI points [1..65535] */ + j = sscanf(optarg, "%u", &arg_u); + if ((j != 1) || (arg_u < 1) || (arg_u > 65535)) { + printf("ERROR: argument parsing of -n argument. -h for help.\n"); + return EXIT_FAILURE; + } else { + rssi_pts = (uint16_t)arg_u; + } + break; + + case 'o': /* -o <int> SX127x RSSI offset [-128..127] */ + j = sscanf(optarg, "%i", &arg_i); + if ((j != 1) || (arg_i < -128) || (arg_i > 127)) { + printf("ERROR: argument parsing of -o argument. -h for help.\n"); + return EXIT_FAILURE; + } else { + rssi_offset = (int8_t)arg_i; + } + break; + + case 'l': /* -l <char> Log file name */ + j = sscanf(optarg, "%s", arg_s); + if (j != 1) { + printf("ERROR: argument parsing of -l argument. -h for help.\n"); + return EXIT_FAILURE; + } else { + sprintf(log_file_name, "%s", arg_s); + } + break; + + default: + printf("ERROR: argument parsing options. -h for help.\n"); + return EXIT_FAILURE; + } + } + + /* Start message */ + printf("+++ Start spectral scan of LoRa gateway channels +++\n"); + + x = lgw_connect(true, 0); /* SPI only, no FPGA reset/configure (for now) */ + if(x != 0) { + printf("ERROR: Failed to connect to FPGA\n"); + return EXIT_FAILURE; + } + + /* Check if FPGA supports Spectral Scan */ + lgw_fpga_reg_r(LGW_FPGA_FEATURE, ®_val); + if (TAKE_N_BITS_FROM((uint8_t)reg_val, FPGA_FEATURE_SPECTRAL_SCAN, 1) != true) { + printf("ERROR: Spectral Scan is not supported (0x%x)\n", (uint8_t)reg_val); + return EXIT_FAILURE; + } + + /* Check if FPGA supports LBT, in order to apply proper constraints on spectral scan parameters */ + lgw_fpga_reg_r(LGW_FPGA_FEATURE, ®_val); + if (TAKE_N_BITS_FROM((uint8_t)reg_val, FPGA_FEATURE_LBT, 1) == true) { + printf("WARNING: The FPGA supports LBT, so running spectral scan with specific constraints\n"); + printf(" => Check the parameters summary below\n"); + /* Get start frequency from FPGA */ + lgw_fpga_reg_r(LGW_FPGA_LBT_INITIAL_FREQ, ®_val); + switch (reg_val) { + case 0: + init_freq = 915000000; + break; + case 1: + init_freq = 863000000; + break; + default: + printf("ERROR: init frequency %d is not supported\n", reg_val); + return EXIT_FAILURE; + } + + /* Check parameters based on LBT constraints */ + if (start_freq < init_freq) { + printf("ERROR: start frequency %d is not supported, should be >=%d\n", start_freq, init_freq); + return EXIT_FAILURE; + } + if (stop_freq > (init_freq + 255*LBT_MIN_STEP_FREQ)) { + printf("ERROR: stop frequency %d is not supported, should be <%d\n", stop_freq, init_freq + 255*LBT_MIN_STEP_FREQ); + return EXIT_FAILURE; + } + if (step_freq < LBT_MIN_STEP_FREQ) { + printf("ERROR: step frequency %d is not supported, should be >=%d\n", step_freq, LBT_MIN_STEP_FREQ); + return EXIT_FAILURE; + } else { + /* Ensure the given step is a multiple of LBT_MIN_STEP_FREQ */ + step_freq = (step_freq / LBT_MIN_STEP_FREQ) * LBT_MIN_STEP_FREQ; + } + + /* Overload hard-coded spectral scan parameters */ + rssi_pts = LBT_DEFAULT_RSSI_PTS; + + /* Spectral scan sequence is slightly different depending if LBT is there or not */ + lbt_support = true; + } else { + /* Reconnect to FPGA with sw reset and configure */ + x = lgw_disconnect(); + if(x != 0) { + printf("ERROR: Failed to disconnect from FPGA\n"); + return EXIT_FAILURE; + } + x = lgw_connect(false, LGW_DEFAULT_NOTCH_FREQ); /* FPGA reset/configure */ + if(x != 0) { + printf("ERROR: Failed to connect to FPGA\n"); + return EXIT_FAILURE; + } + /* Some spectral scan options are only available when there is no LBT support */ + x = lgw_fpga_reg_w(LGW_FPGA_HISTO_NB_READ, rssi_pts-1); + if( x != LGW_REG_SUCCESS ) + { + printf( "ERROR: Failed to configure FPGA\n" ); + return EXIT_FAILURE; + } + + /* Initialize frequency */ + freq_reg = ((uint64_t)start_freq << 19) / (uint64_t)32000000; + lgw_fpga_reg_w(LGW_FPGA_HISTO_SCAN_FREQ, (int32_t)freq_reg); + } + + /* create log file */ + strcat(log_file_name,".csv"); + log_file = fopen(log_file_name, "w"); + if (log_file == NULL) { + printf("ERROR: impossible to create log file %s\n", log_file_name); + return EXIT_FAILURE; + } + printf("Writing to file: %s\n", log_file_name); + + /* Number of frequency steps */ + freq_nb = (int)((stop_freq - start_freq) / step_freq) + 1; + printf("Scanning frequencies:\nstart: %d Hz\nstop : %d Hz\nstep : %d Hz\nnb : %d\n", start_freq, stop_freq, step_freq, freq_nb); + + /* Main loop */ + for(j = 0; j < freq_nb; j++) { + /* Current frequency */ + freq = start_freq + j * step_freq; + printf("%d", freq); + + if (lbt_support == false) { + /* Set SX127x */ + x = lgw_setup_sx127x(freq, MOD_FSK, channel_bw_khz, rssi_offset); + if( x != 0 ) + { + printf( "ERROR: SX127x setup failed\n" ); + return EXIT_FAILURE; + } + + /* Start FPGA state machine for spectral scal */ + lgw_fpga_reg_w(LGW_FPGA_CTRL_FEATURE_START, 1); + } else { + /* Do Nothing */ + /* LBT setup has already done the necessary */ + } + + /* Clean histogram */ + lgw_fpga_reg_w(LGW_FPGA_CTRL_CLEAR_HISTO_MEM, 1); + + /* Wait for histogram clean to start */ + do { + wait_ms(10); + lgw_fpga_reg_r(LGW_FPGA_STATUS, ®_val); + } + while((TAKE_N_BITS_FROM((uint8_t)reg_val, 0, 5)) != 1); /* Clear has started */ + + /* Set scan frequency during clear process */ + if (lbt_support == false) { + /* We can directly set the scan frequency */ + freq_reg = ((uint64_t)freq << 19) / (uint64_t)32000000; + lgw_fpga_reg_w(LGW_FPGA_HISTO_SCAN_FREQ, (int32_t)freq_reg); + } else { + /* The possible scan frequencies are hard-coded in FPGA, we give an offset from init_freq */ + freq_idx = (freq - init_freq) / LBT_MIN_STEP_FREQ; + printf(" (idx=%i) ", freq_idx); + lgw_fpga_reg_w(LGW_FPGA_SCAN_FREQ_OFFSET, freq_idx); + } + + /* Release FPGA state machine */ + lgw_fpga_reg_w(LGW_FPGA_CTRL_CLEAR_HISTO_MEM, 0); + + /* Wait for histogram ready */ + do { + wait_ms(1000); + lgw_fpga_reg_r(LGW_FPGA_STATUS, ®_val); + } + while((TAKE_N_BITS_FROM((uint8_t)reg_val, 5, 1)) != 1); + + if (lbt_support == false) { + /* Stop FPGA state machine for spectral scan */ + lgw_fpga_reg_w(LGW_FPGA_CTRL_FEATURE_START, 0); + } else { + /* Do Nothing */ + /* LBT is running */ + } + + /* Read histogram */ + lgw_fpga_reg_w(LGW_FPGA_CTRL_ACCESS_HISTO_MEM, 1); /* HOST gets access to FPGA RAM */ + lgw_fpga_reg_w(LGW_FPGA_HISTO_RAM_ADDR, 0); + lgw_fpga_reg_rb(LGW_FPGA_HISTO_RAM_DATA, read_burst, RSSI_RANGE*2); + lgw_fpga_reg_w(LGW_FPGA_CTRL_ACCESS_HISTO_MEM, 0); /* FPGA gets access to RAM back */ + + /* Write data to CSV */ + fprintf(log_file, "%d", freq); + rssi_cumu = 0; + k = 0; + for (i = 0; i < RSSI_RANGE; i++) { + rssi_histo = (uint16_t)read_burst[2*i] | ((uint16_t)read_burst[2*i+1] << 8); + fprintf(log_file, ",%.1f,%d", -i/2.0, rssi_histo); + rssi_cumu += rssi_histo; + if (rssi_cumu > rssi_pts) { + printf(" - WARNING: number of RSSI points higher than expected (%u,%u)", rssi_cumu, rssi_pts); + rssi_cumu = rssi_pts; + } + if (rssi_cumu > rssi_thresh[k]*rssi_pts) { + printf(" %d%%<%.1f", (uint16_t)(rssi_thresh[k]*100), -i/2.0); + k++; + } + } + fprintf(log_file, "\n"); + printf("\n"); + } + fclose(log_file); + + /* Close SPI */ + x = lgw_disconnect(); + if(x != 0) { + printf("ERROR: Failed to disconnect FPGA\n"); + return EXIT_FAILURE; + } + + printf("+++ Exiting Spectral scan program +++\n"); + + return EXIT_SUCCESS; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* --- EOF ------------------------------------------------------------------ */ + diff --git a/util_spi_stress/Makefile b/util_spi_stress/Makefile new file mode 100644 index 0000000..23e3c88 --- /dev/null +++ b/util_spi_stress/Makefile @@ -0,0 +1,66 @@ +### Application-specific constants + +APP_NAME := util_spi_stress + +### Environment constants + +LGW_PATH ?= ../libloragw +ARCH ?= +CROSS_COMPILE ?= + +### External constant definitions +# must get library build option to know if mpsse must be linked or not + +include $(LGW_PATH)/library.cfg + +### Constant symbols + +CC := $(CROSS_COMPILE)gcc +AR := $(CROSS_COMPILE)ar + +CFLAGS=-O2 -Wall -Wextra -std=c99 -Iinc -I. + +OBJDIR = obj + +### Constants for LoRa concentrator HAL library +# List the library sub-modules that are used by the application + +LGW_INC = $(LGW_PATH)/inc/config.h +LGW_INC += $(LGW_PATH)/inc/loragw_reg.h + +### Linking options + +LIBS := -lloragw -lrt -lm + +### General build targets + +all: $(APP_NAME) + +clean: + rm -f $(OBJDIR)/*.o + rm -f $(APP_NAME) + +### HAL library (do no force multiple library rebuild even with 'make -B') + +$(LGW_PATH)/inc/config.h: + @if test ! -f $@; then \ + $(MAKE) all -C $(LGW_PATH); \ + fi + +$(LGW_PATH)/libloragw.a: $(LGW_INC) + @if test ! -f $@; then \ + $(MAKE) all -C $(LGW_PATH); \ + fi + +### Main program compilation and assembly + +$(OBJDIR): + mkdir -p $(OBJDIR) + +$(OBJDIR)/$(APP_NAME).o: src/$(APP_NAME).c $(LGW_INC) | $(OBJDIR) + $(CC) -c $(CFLAGS) -I$(LGW_PATH)/inc $< -o $@ + +$(APP_NAME): $(OBJDIR)/$(APP_NAME).o $(LGW_PATH)/libloragw.a + $(CC) -L$(LGW_PATH) $< -o $@ $(LIBS) + +### EOF diff --git a/util_spi_stress/readme.md b/util_spi_stress/readme.md new file mode 100644 index 0000000..7848b0f --- /dev/null +++ b/util_spi_stress/readme.md @@ -0,0 +1,93 @@ + / _____) _ | | + ( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | + (______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +LoRa concentrator SPI stress test +================================== + +1. Introduction +---------------- + +This software is used to check the reliability of the link between the host +platform (on which the program is run) and the LoRa concentrator register file +that is the interface through which all interaction with the LoRa concentrator +happens. + +2. Dependencies +---------------- + +This program only access the LoRa concentrator HAL library through its +loragw_reg "named registers" access sub-module. + +It was tested with v1.3.0 of the libloragw library, and should be compatible +with any later version of the library and the hardware, assuming the registers +used for the tests are still present. + +The registers used are: + * LGW_VERSION + * LGW_IMPLICIT_PAYLOAD_LENGHT + * LGW_FSK_REF_PATTERN_LSB + * LGW_RX_DATA_BUF_ADDR + * LGW_RX_DATA_BUF_DATA + +A data buffer accessible through the 2 registers above must be implemented. + +3. Usage +--------- + +The tests run forever or until an error is detected. +Press Ctrl+C to stop the application. + +When an error is detected, diagnosis information are displayed. Please refer to +the source code for more details on what is displayed for diagnosis. + +All tests use pseudo-random data generated by the rand() function. The random +generator is not seeded, and the same sequence of data will be use each time the +program is launched. + +Basically, some random data is written, read back and then compared to the +initial written data. Some "useless" read on others registers might be inserted +to be sure that the data read back is coming from the hardware, and not from the +internal buffer(s) of the software driver(s). + +Test 1 > R/W on a simple 8-bit register + +Test 2 > R/W on a simple 8-bit register with interstitial reads on VERSION + +Test 3 > R/W on a 32-bit register (short SPI bursts access) + +Test 4 > data buffer R/W (long SPI bursts access) + +4. License +----------- + +Copyright (c) 2013, SEMTECH S.A. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of the Semtech corporation nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*EOF*
\ No newline at end of file diff --git a/util_spi_stress/src/util_spi_stress.c b/util_spi_stress/src/util_spi_stress.c new file mode 100644 index 0000000..6cc5f04 --- /dev/null +++ b/util_spi_stress/src/util_spi_stress.c @@ -0,0 +1,292 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + SPI stress test + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +/* fix an issue between POSIX and C99 */ +#if __STDC_VERSION__ >= 199901L + #define _XOPEN_SOURCE 600 +#else + #define _XOPEN_SOURCE 500 +#endif + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ +#include <stdio.h> /* printf fprintf sprintf fopen fputs */ + +#include <signal.h> /* sigaction */ +#include <unistd.h> /* getopt access */ +#include <stdlib.h> /* rand */ + +#include "loragw_reg.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#define MSG(args...) fprintf(stderr, args) /* message that is destined to the user */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +#define VERS 103 +#define READS_WHEN_ERROR 16 /* number of times a read is repeated if there is a read error */ +#define BUFF_SIZE 1024 /* maximum number of bytes that we can write in sx1301 RX data buffer */ +#define DEFAULT_TX_NOTCH_FREQ 129E3 + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE VARIABLES (GLOBAL) ------------------------------------------- */ + +/* signal handling variables */ +struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */ +static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */ +static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ + +static void sig_handler(int sigio); + +void usage (void); + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ + +static void sig_handler(int sigio) { + if (sigio == SIGQUIT) { + quit_sig = 1;; + } else if ((sigio == SIGINT) || (sigio == SIGTERM)) { + exit_sig = 1; + } +} + +/* describe command line options */ +void usage(void) { + MSG( "Available options:\n"); + MSG( " -h print this help\n"); + MSG( " -t <int> specify which test you want to run (1-4)\n"); +} + +/* -------------------------------------------------------------------------- */ +/* --- MAIN FUNCTION -------------------------------------------------------- */ + +int main(int argc, char **argv) +{ + int i; + int xi = 0; + + /* application option */ + int test_number = 1; + int cycle_number = 0; + int repeats_per_cycle = 1000; + bool error = false; + + /* in/out variables */ + int32_t test_value; + int32_t read_value; + int32_t rb1, rb2, rb3; /* interstitial readbacks, to flush buffers if needed */ + + /* data buffer */ + int32_t test_addr; + uint8_t test_buff[BUFF_SIZE]; + uint8_t read_buff[BUFF_SIZE]; + + /* parse command line options */ + while ((i = getopt (argc, argv, "ht:")) != -1) { + switch (i) { + case 'h': + usage(); + return EXIT_FAILURE; + break; + + case 't': + i = sscanf(optarg, "%i", &xi); + if ((i != 1) || (xi < 1) || (xi > 4)) { + MSG("ERROR: invalid test number\n"); + return EXIT_FAILURE; + } else { + test_number = xi; + } + break; + + default: + MSG("ERROR: argument parsing use -h option for help\n"); + usage(); + return EXIT_FAILURE; + } + } + MSG("INFO: Starting LoRa concentrator SPI stress-test number %i\n", test_number); + + /* configure signal handling */ + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_handler = sig_handler; + sigaction(SIGQUIT, &sigact, NULL); + sigaction(SIGINT, &sigact, NULL); + sigaction(SIGTERM, &sigact, NULL); + + /* start SPI link */ + i = lgw_connect(false, DEFAULT_TX_NOTCH_FREQ); + if (i != LGW_REG_SUCCESS) { + MSG("ERROR: lgw_connect() did not return SUCCESS"); + return EXIT_FAILURE; + } + + if (test_number == 1) { + /* single 8b register R/W stress test */ + while ((quit_sig != 1) && (exit_sig != 1)) { + printf("Cycle %i > ", cycle_number); + for (i=0; i<repeats_per_cycle; ++i) { + test_value = (rand() % 256); + lgw_reg_w(LGW_IMPLICIT_PAYLOAD_LENGHT, test_value); + lgw_reg_r(LGW_IMPLICIT_PAYLOAD_LENGHT, &read_value); + if (read_value != test_value) { + error = true; + break; + } + } + if (error) { + printf("error during the %ith iteration: write 0x%02X, read 0x%02X\n", i+1, test_value, read_value); + printf("Repeat read of target register:"); + for (i=0; i<READS_WHEN_ERROR; ++i) { + lgw_reg_r(LGW_IMPLICIT_PAYLOAD_LENGHT, &read_value); + printf(" 0x%02X", read_value); + } + printf("\n"); + return EXIT_FAILURE; + } else { + printf("did %i R/W on an 8 bits reg with no error\n", repeats_per_cycle); + ++cycle_number; + } + } + } else if (test_number == 2) { + /* single 8b register R/W with interstitial VERSION check stress test */ + while ((quit_sig != 1) && (exit_sig != 1)) { + printf("Cycle %i > ", cycle_number); + for (i=0; i<repeats_per_cycle; ++i) { + test_value = (rand() % 256); + lgw_reg_r(LGW_VERSION, &rb1); + lgw_reg_w(LGW_IMPLICIT_PAYLOAD_LENGHT, test_value); + lgw_reg_r(LGW_VERSION, &rb2); + lgw_reg_r(LGW_IMPLICIT_PAYLOAD_LENGHT, &read_value); + lgw_reg_r(LGW_VERSION, &rb3); + if ((rb1 != VERS) || (rb2 != VERS) || (rb3 != VERS) || (read_value != test_value)) { + error = true; + break; + } + } + if (error) { + printf("error during the %ith iteration: write %02X, read %02X, version (%i, %i, %i)\n", i+1, test_value, read_value, rb1, rb2, rb3); + printf("Repeat read of target register:"); + for (i=0; i<READS_WHEN_ERROR; ++i) { + lgw_reg_r(LGW_IMPLICIT_PAYLOAD_LENGHT, &read_value); + printf(" 0x%02X", read_value); + } + printf("\n"); + return EXIT_FAILURE; + } else { + printf("did %i R/W on an 8 bits reg with no error\n", repeats_per_cycle); + ++cycle_number; + } + } + } else if (test_number == 3) { + /* 32b register R/W stress test */ + while ((quit_sig != 1) && (exit_sig != 1)) { + printf("Cycle %i > ", cycle_number); + for (i=0; i<repeats_per_cycle; ++i) { + test_value = (rand() & 0x0000FFFF); + test_value += (int32_t)(rand() & 0x0000FFFF) << 16; + lgw_reg_w(LGW_FSK_REF_PATTERN_LSB, test_value); + lgw_reg_r(LGW_FSK_REF_PATTERN_LSB, &read_value); + if (read_value != test_value) { + error = true; + break; + } + } + if (error) { + printf("error during the %ith iteration: write 0x%08X, read 0x%08X\n", i+1, test_value, read_value); + printf("Repeat read of target register:"); + for (i=0; i<READS_WHEN_ERROR; ++i) { + lgw_reg_r(LGW_FSK_REF_PATTERN_LSB, &read_value); + printf(" 0x%08X", read_value); + } + printf("\n"); + return EXIT_FAILURE; + } else { + printf("did %i R/W on a 32 bits reg with no error\n", repeats_per_cycle); + ++cycle_number; + } + } + } else if (test_number == 4) { + /* databuffer R/W stress test */ + while ((quit_sig != 1) && (exit_sig != 1)) { + for (i=0; i<BUFF_SIZE; ++i) { + test_buff[i] = rand() & 0xFF; + } + printf("Cycle %i > ", cycle_number); + test_addr = rand() & 0xFFFF; + lgw_reg_w(LGW_RX_DATA_BUF_ADDR, test_addr); /* write at random offset in memory */ + lgw_reg_wb(LGW_RX_DATA_BUF_DATA, test_buff, BUFF_SIZE); + lgw_reg_w(LGW_RX_DATA_BUF_ADDR, test_addr); /* go back to start of segment */ + lgw_reg_rb(LGW_RX_DATA_BUF_DATA, read_buff, BUFF_SIZE); + for (i=0; ((i<BUFF_SIZE) && (test_buff[i] == read_buff[i])); ++i); + if (i != BUFF_SIZE) { + printf("error during the buffer comparison\n"); + printf("Written values:\n"); + for (i=0; i<BUFF_SIZE; ++i) { + printf(" %02X ", test_buff[i]); + if (i%16 == 15) printf("\n"); + } + printf("\n"); + printf("Read values:\n"); + for (i=0; i<BUFF_SIZE; ++i) { + printf(" %02X ", read_buff[i]); + if (i%16 == 15) printf("\n"); + } + printf("\n"); + lgw_reg_w(LGW_RX_DATA_BUF_ADDR, test_addr); /* go back to start of segment */ + lgw_reg_rb(LGW_RX_DATA_BUF_DATA, read_buff, BUFF_SIZE); + printf("Re-read values:\n"); + for (i=0; i<BUFF_SIZE; ++i) { + printf(" %02X ", read_buff[i]); + if (i%16 == 15) printf("\n"); + } + printf("\n"); + return EXIT_FAILURE; + } else { + printf("did a %i-byte R/W on a data buffer with no error\n", BUFF_SIZE); + ++cycle_number; + } + } + } else { + MSG("ERROR: invalid test number"); + usage(); + } + + /* close SPI link */ + i = lgw_disconnect(); + if (i != LGW_REG_SUCCESS) { + MSG("ERROR: lgw_disconnect() did not return SUCCESS"); + return EXIT_FAILURE; + } + + MSG("INFO: Exiting LoRa concentrator SPI stress-test program\n"); + return EXIT_SUCCESS; +} + +/* --- EOF ------------------------------------------------------------------ */ + diff --git a/util_tx_continuous/Makefile b/util_tx_continuous/Makefile new file mode 100644 index 0000000..b07419b --- /dev/null +++ b/util_tx_continuous/Makefile @@ -0,0 +1,68 @@ +### Application-specific constants + +APP_NAME := util_tx_continuous + +### Environment constants + +LGW_PATH ?= ../libloragw +ARCH ?= +CROSS_COMPILE ?= + +### External constant definitions +# must get library build option to know if mpsse must be linked or not + +include $(LGW_PATH)/library.cfg + +### Constant symbols + +CC := $(CROSS_COMPILE)gcc +AR := $(CROSS_COMPILE)ar + +CFLAGS=-O2 -Wall -Wextra -std=c99 -Iinc -I. + +OBJDIR = obj + +### Constants for LoRa concentrator HAL library +# List the library sub-modules that are used by the application + +LGW_INC = $(LGW_PATH)/inc/config.h +LGW_INC += $(LGW_PATH)/inc/loragw_hal.h +LGW_INC += $(LGW_PATH)/inc/loragw_reg.h +LGW_INC += $(LGW_PATH)/inc/loragw_aux.h + +### Linking options + +LIBS := -lloragw -lrt -lm + +### General build targets + +all: $(APP_NAME) + +clean: + rm -f $(OBJDIR)/*.o + rm -f $(APP_NAME) + +### HAL library (do no force multiple library rebuild even with 'make -B') + +$(LGW_PATH)/inc/config.h: + @if test ! -f $@; then \ + $(MAKE) all -C $(LGW_PATH); \ + fi + +$(LGW_PATH)/libloragw.a: $(LGW_INC) + @if test ! -f $@; then \ + $(MAKE) all -C $(LGW_PATH); \ + fi + +### Main program compilation and assembly + +$(OBJDIR): + mkdir -p $(OBJDIR) + +$(OBJDIR)/$(APP_NAME).o: src/$(APP_NAME).c $(LGW_INC) | $(OBJDIR) + $(CC) -c $(CFLAGS) -I$(LGW_PATH)/inc $< -o $@ + +$(APP_NAME): $(OBJDIR)/$(APP_NAME).o $(LGW_PATH)/libloragw.a + $(CC) -L$(LGW_PATH) $< -o $@ $(LIBS) + +### EOF diff --git a/util_tx_continuous/readme.md b/util_tx_continuous/readme.md new file mode 100644 index 0000000..70a75ba --- /dev/null +++ b/util_tx_continuous/readme.md @@ -0,0 +1,62 @@ + ______ _ + / _____) _ | | + ( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | + (______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2014 Semtech-Cycleo + +Tx continuous program for LoRa concentrator +=========================================== + + +1. Introduction +---------------- + +This software is used to set LoRa concentrator in Tx continuous mode, for +spectral measurement. +The user can set the modulation type, the modulation parameters, and the +multiple gains of the Tx chain. +The program runs indefinitely, until the user stops the application. + + +2. Usage +-------- + +See command line help to get the list of all available options: +./util_tx_continuous -h + +Example: +./util_tx_continuous -f 868 -r 1257 --dig 0 --mix 14 --pa 3 --mod "LORA" --sf 7 --bw 125 + + +4. License +----------- + +Copyright (c) 2013, SEMTECH S.A. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of the Semtech corporation nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*EOF* diff --git a/util_tx_continuous/src/util_tx_continuous.c b/util_tx_continuous/src/util_tx_continuous.c new file mode 100644 index 0000000..b081cce --- /dev/null +++ b/util_tx_continuous/src/util_tx_continuous.c @@ -0,0 +1,454 @@ +/* + ______ _ + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2014 Semtech-Cycleo + +Description: + SX1301 tx continuous utility + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Matthieu Leurent +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDENCIES --------------------------------------------------------- */ + +/* Fix an issue between POSIX and C99 */ +#if __STDC_VERSION__ >= 199901L + #define _XOPEN_SOURCE 600 +#else + #define _XOPEN_SOURCE 500 +#endif + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ +#include <stdio.h> /* printf fprintf sprintf fopen fputs */ +#include <string.h> /* memset */ +#include <signal.h> /* sigaction */ +#include <unistd.h> /* getopt access */ +#include <stdlib.h> /* exit codes */ +#include <getopt.h> /* getopt_long */ + +#include "loragw_hal.h" +#include "loragw_reg.h" +#include "loragw_aux.h" + +/* -------------------------------------------------------------------------- */ +/* --- MACROS & CONSTANTS --------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#define MSG(args...) fprintf(stderr, args) /* message that is destined to the user */ + +#define TX_RF_CHAIN 0 /* TX only supported on radio A */ +#define DEFAULT_RSSI_OFFSET 0.0 + +#define DEFAULT_FREQ_HZ 868e6 +#define DEFAULT_DIGITAL_GAIN 0 +#define DEFAULT_DAC_GAIN 3 +#define DEFAULT_MIXER_GAIN 14 +#define DEFAULT_PA_GAIN 3 +#define DEFAULT_MODULATION "LORA" +#define DEFAULT_SF 7 +#define DEFAULT_BW_KHZ 125 +#define DEFAULT_BR_KBPS 50 +#define DEFAULT_FDEV_KHZ 25 +#define DEFAULT_BT 2 +#define DEFAULT_NOTCH_FREQ 129000U + +/* -------------------------------------------------------------------------- */ +/* --- GLOBAL VARIABLES ----------------------------------------------------- */ + +/* Signal handling variables */ +static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */ +static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */ + +/* -------------------------------------------------------------------------- */ +/* --- SUBFUNCTIONS DECLARATION --------------------------------------------- */ + +static void sig_handler(int sigio); + +/* -------------------------------------------------------------------------- */ +/* --- MAIN FUNCTION -------------------------------------------------------- */ + +int main(int argc, char **argv) +{ + static struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */ + + int i; /* loop and temporary variables */ + + /* Parameter parsing */ + int option_index = 0; + static struct option long_options[] = { + {"dig", 1, 0, 0}, + {"dac", 1, 0, 0}, + {"mix", 1, 0, 0}, + {"pa", 1, 0, 0}, + {"mod", 1, 0, 0}, + {"sf", 1, 0, 0}, + {"bw", 1, 0, 0}, + {"br", 1, 0, 0}, + {"fdev", 1, 0, 0}, + {"bt", 1, 0, 0}, + {"notch", 1, 0, 0}, + {0, 0, 0, 0} + }; + unsigned int arg_u; + float arg_f; + char arg_s[64]; + + /* Application parameters */ + uint32_t freq_hz = DEFAULT_FREQ_HZ; + uint8_t g_dig = DEFAULT_DIGITAL_GAIN; + uint8_t g_dac = DEFAULT_DAC_GAIN; + uint8_t g_mix = DEFAULT_MIXER_GAIN; + uint8_t g_pa = DEFAULT_PA_GAIN; + char mod[64] = DEFAULT_MODULATION; + uint8_t sf = DEFAULT_SF; + unsigned int bw_khz = DEFAULT_BW_KHZ; + float br_kbps = DEFAULT_BR_KBPS; + uint8_t fdev_khz = DEFAULT_FDEV_KHZ; + uint8_t bt = DEFAULT_BT; + uint32_t tx_notch_freq = DEFAULT_NOTCH_FREQ; + + int32_t offset_i, offset_q; + + /* RF configuration (TX fail if RF chain is not enabled) */ + enum lgw_radio_type_e radio_type = LGW_RADIO_TYPE_SX1257; + struct lgw_conf_board_s boardconf; + struct lgw_conf_rxrf_s rfconf; + struct lgw_tx_gain_lut_s txlut; + struct lgw_pkt_tx_s txpkt; + + + /* Parse command line options */ + while ((i = getopt_long (argc, argv, "hud::f:r:", long_options, &option_index)) != -1) { + switch (i) { + case 'h': + printf("~~~ Library version string~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); + printf(" %s\n", lgw_version_info()); + printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); + printf(" -f <float> Tx RF frequency in MHz [800:1000]\n"); + printf(" -r <int> Radio type (SX1255:1255, SX1257:1257)\n"); + printf(" --notch <uint> Tx notch filter frequency in KhZ [126..250]\n"); + printf(" --dig <uint> Digital gain trim, [0:3]\n"); + printf(" 0:1, 1:7/8, 2:3/4, 3:1/2\n"); + printf(" --mix <uint> Radio Tx mixer gain trim, [0:15]\n"); + printf(" 15 corresponds to maximum gain, 1 LSB corresponds to 2dB step\n"); + printf(" --pa <uint> PA gain trim, [0:3]\n"); + printf(" --mod <char> Modulation type ['LORA','FSK','CW']\n"); + printf(" --sf <uint> LoRa Spreading Factor, [7:12]\n"); + printf(" --bw <uint> LoRa bandwidth in kHz, [125,250,500]\n"); + printf(" --br <float> FSK bitrate in kbps, [0.5:250]\n"); + printf(" --fdev <uint> FSK frequency deviation in kHz, [1:250]\n"); + printf(" --bt <uint> FSK gaussian filter BT trim, [0:3]\n"); + printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); + return EXIT_SUCCESS; + break; + + case 0: + if (strcmp(long_options[option_index].name,"dig") == 0) { + i = sscanf(optarg, "%u", &arg_u); + if ((i != 1) || (arg_u > 3)) { + printf("ERROR: argument parsing of --dig argument. Use -h to print help\n"); + return EXIT_FAILURE; + } + else + { + g_dig = (uint8_t)arg_u; + } + } + else if (strcmp(long_options[option_index].name,"dac") == 0) { + i = sscanf(optarg, "%u", &arg_u); + if ((i != 1) || (arg_u > 3)) { + printf("ERROR: argument parsing of --dac argument. Use -h to print help\n"); + return EXIT_FAILURE; + } + else { + g_dac = (uint8_t)arg_u; + } + } + else if (strcmp(long_options[option_index].name,"mix") == 0) { + i = sscanf(optarg, "%u", &arg_u); + if ((i != 1) || (arg_u > 15)) { + printf("ERROR: argument parsing of --mix argument. Use -h to print help\n"); + return EXIT_FAILURE; + } + else { + g_mix = (uint8_t)arg_u; + } + } + else if (strcmp(long_options[option_index].name,"pa") == 0) { + i = sscanf(optarg, "%u", &arg_u); + if ((i != 1) || (arg_u > 3)) { + printf("ERROR: argument parsing of --pa argument. Use -h to print help\n"); + return EXIT_FAILURE; + } + else { + g_pa = arg_u; + } + } + else if (strcmp(long_options[option_index].name,"mod") == 0) { + i = sscanf(optarg, "%s", arg_s); + if ((i != 1) || ((strcmp(arg_s,"LORA") != 0) && (strcmp(arg_s,"FSK") != 0) && (strcmp(arg_s,"CW") != 0))) { + printf("ERROR: argument parsing of --mod argument. Use -h to print help\n"); + return EXIT_FAILURE; + } + else { + sprintf(mod, "%s", arg_s); + } + } + else if (strcmp(long_options[option_index].name,"sf") == 0) { + i = sscanf(optarg, "%u", &arg_u); + if ((i != 1) || (arg_u < 7) || (arg_u > 12)) { + printf("ERROR: argument parsing of --sf argument. Use -h to print help\n"); + return EXIT_FAILURE; + } + else { + sf = (uint8_t)arg_u; + } + } + else if (strcmp(long_options[option_index].name,"bw") == 0) { + i = sscanf(optarg, "%u", &arg_u); + if ((i != 1) || ((arg_u != 125) && (arg_u != 250) && (arg_u != 500))) { + printf("ERROR: argument parsing of --bw argument. Use -h to print help\n"); + return EXIT_FAILURE; + } + else { + bw_khz = arg_u; + } + } + else if (strcmp(long_options[option_index].name,"br") == 0) { + i = sscanf(optarg, "%f", &arg_f); + if ((i != 1) || (arg_f < 0.5) || (arg_f > 250)) { + printf("ERROR: argument parsing of --br argument. Use -h to print help\n"); + return EXIT_FAILURE; + } + else { + br_kbps = arg_f; + } + } + else if (strcmp(long_options[option_index].name,"fdev") == 0) { + i = sscanf(optarg, "%u", &arg_u); + if ((i != 1) || (arg_u < 1) || (arg_u > 250)) { + printf("ERROR: argument parsing of --fdev argument. Use -h to print help\n"); + return EXIT_FAILURE; + } + else { + fdev_khz = (uint8_t)arg_u; + } + } + else if (strcmp(long_options[option_index].name,"bt") == 0) { + i = sscanf(optarg, "%u", &arg_u); + if ((i != 1) || (arg_u > 3)) { + printf("ERROR: argument parsing of --bt argument. Use -h to print help\n"); + return EXIT_FAILURE; + } + else { + bt = (uint8_t)arg_u; + } + } + else if (strcmp(long_options[option_index].name,"notch") == 0) { + i = sscanf(optarg, "%u", &arg_u); + if ((i != 1) || ((arg_u < 126) || (arg_u > 250))) { + printf("ERROR: argument parsing of --notch argument. Use -h to print help\n"); + return EXIT_FAILURE; + } + else { + tx_notch_freq = (uint32_t)arg_u * 1000U; + } + } + else { + printf("ERROR: argument parsing options. Use -h to print help\n"); + return EXIT_FAILURE; + } + break; + + case 'f': + i = sscanf(optarg, "%f", &arg_f); + if ((i != 1) || (arg_f < 1)) { + printf("ERROR: argument parsing of -f argument. Use -h to print help\n"); + return EXIT_FAILURE; + } + else { + freq_hz = (uint32_t)((arg_f * 1e6) + 0.5); + } + break; + + case 'r': + i = sscanf(optarg, "%u", &arg_u); + switch (arg_u) { + case 1255: + radio_type = LGW_RADIO_TYPE_SX1255; + break; + case 1257: + radio_type = LGW_RADIO_TYPE_SX1257; + break; + default: + printf("ERROR: argument parsing of -r argument. Use -h to print help\n"); + return EXIT_FAILURE; + } + break; + + default: + printf("ERROR: argument parsing options. Use -h to print help\n"); + return EXIT_FAILURE; + } + } + + /* Configure signal handling */ + sigemptyset( &sigact.sa_mask ); + sigact.sa_flags = 0; + sigact.sa_handler = sig_handler; + sigaction( SIGQUIT, &sigact, NULL ); + sigaction( SIGINT, &sigact, NULL ); + sigaction( SIGTERM, &sigact, NULL ); + + /* Board config */ + memset(&boardconf, 0, sizeof(boardconf)); + boardconf.lorawan_public = true; + boardconf.clksrc = 1; /* Radio B is source by default */ + lgw_board_setconf(boardconf); + + /* RF config */ + memset(&rfconf, 0, sizeof(rfconf)); + rfconf.enable = true; + rfconf.freq_hz = freq_hz; + rfconf.rssi_offset = DEFAULT_RSSI_OFFSET; + rfconf.type = radio_type; + rfconf.tx_enable = true; + rfconf.tx_notch_freq = tx_notch_freq; + lgw_rxrf_setconf(TX_RF_CHAIN, rfconf); + + /* Tx gain LUT */ + memset(&txlut, 0, sizeof txlut); + txlut.size = 1; + txlut.lut[0].dig_gain = g_dig; + txlut.lut[0].pa_gain = g_pa; + txlut.lut[0].dac_gain = g_dac; + txlut.lut[0].mix_gain = g_mix; + txlut.lut[0].rf_power = 0; + lgw_txgain_setconf(&txlut); + + /* Start the concentrator */ + i = lgw_start(); + if (i == LGW_HAL_SUCCESS) { + MSG("INFO: concentrator started, packet can be sent\n"); + } else { + MSG("ERROR: failed to start the concentrator\n"); + return EXIT_FAILURE; + } + + /* fill-up payload and parameters */ + memset(&txpkt, 0, sizeof(txpkt)); + txpkt.freq_hz = freq_hz; + txpkt.tx_mode = IMMEDIATE; + txpkt.rf_chain = TX_RF_CHAIN; + txpkt.rf_power = 0; + if (strcmp(mod, "FSK") == 0) { + txpkt.modulation = MOD_FSK; + txpkt.datarate = br_kbps * 1e3; + } else { + txpkt.modulation = MOD_LORA; + switch (bw_khz) { + case 125: txpkt.bandwidth = BW_125KHZ; break; + case 250: txpkt.bandwidth = BW_250KHZ; break; + case 500: txpkt.bandwidth = BW_500KHZ; break; + default: + MSG("ERROR: invalid 'bw' variable\n"); + return EXIT_FAILURE; + } + switch (sf) { + case 7: txpkt.datarate = DR_LORA_SF7; break; + case 8: txpkt.datarate = DR_LORA_SF8; break; + case 9: txpkt.datarate = DR_LORA_SF9; break; + case 10: txpkt.datarate = DR_LORA_SF10; break; + case 11: txpkt.datarate = DR_LORA_SF11; break; + case 12: txpkt.datarate = DR_LORA_SF12; break; + default: + MSG("ERROR: invalid 'sf' variable\n"); + return EXIT_FAILURE; + } + } + txpkt.coderate = CR_LORA_4_5; + txpkt.f_dev = fdev_khz; + txpkt.preamble = 65535; + txpkt.invert_pol = false; + txpkt.no_crc = true; + txpkt.no_header = true; + txpkt.size = 1; + txpkt.payload[0] = 0; + + /* Overwrite settings */ + lgw_reg_w(LGW_TX_MODE, 1); /* Tx continuous */ + lgw_reg_w(LGW_FSK_TX_GAUSSIAN_SELECT_BT, bt); + if (strcmp(mod, "CW") == 0) { + /* Enable signal generator with DC */ + lgw_reg_w(LGW_SIG_GEN_FREQ, 0); + lgw_reg_w(LGW_SIG_GEN_EN, 1); + lgw_reg_w(LGW_TX_OFFSET_I, 0); + lgw_reg_w(LGW_TX_OFFSET_Q, 0); + } + + /* Send packet */ + i = lgw_send(txpkt); + + /* Recap all settings */ + printf("SX1301 library version: %s\n", lgw_version_info()); + if (strcmp(mod, "LORA") == 0) { + printf("Modulation: LORA SF:%d BW:%d kHz\n", sf, bw_khz); + } + else if (strcmp(mod, "FSK") == 0) { + printf("Modulation: FSK BR:%3.3f kbps FDEV:%d kHz BT:%d\n", br_kbps, fdev_khz, bt); + } + else if (strcmp(mod, "CW") == 0) { + printf("Modulation: CW\n"); + } + switch(rfconf.type) { + case LGW_RADIO_TYPE_SX1255: + printf("Radio Type: SX1255\n"); + break; + case LGW_RADIO_TYPE_SX1257: + printf("Radio Type: SX1257\n"); + break; + default: + printf("ERROR: undefined radio type\n"); + break; + } + printf("Frequency: %4.3f MHz\n", freq_hz/1e6); + printf("TX Gains: Digital:%d DAC:%d Mixer:%d PA:%d\n", g_dig, g_dac, g_mix, g_pa); + if (strcmp(mod, "CW") != 0) { + lgw_reg_r(LGW_TX_OFFSET_I, &offset_i); + lgw_reg_r(LGW_TX_OFFSET_Q, &offset_q); + printf("Calibrated DC offsets: I:%d Q:%d\n", offset_i, offset_q); + } + + /* waiting for user input */ + while ((quit_sig != 1) && (exit_sig != 1)) { + wait_ms(100); + } + + /* clean up before leaving */ + lgw_stop(); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* --- SUBFUNCTIONS DEFINITION ---------------------------------------------- */ + +static void sig_handler(int sigio) +{ + if (sigio == SIGQUIT) { + quit_sig = 1; + } + else if((sigio == SIGINT) || (sigio == SIGTERM)) { + exit_sig = 1; + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/util_tx_test/Makefile b/util_tx_test/Makefile new file mode 100644 index 0000000..8c35229 --- /dev/null +++ b/util_tx_test/Makefile @@ -0,0 +1,67 @@ +### Application-specific constants + +APP_NAME := util_tx_test + +### Environment constants + +LGW_PATH ?= ../libloragw +ARCH ?= +CROSS_COMPILE ?= + +### External constant definitions +# must get library build option to know if mpsse must be linked or not + +include $(LGW_PATH)/library.cfg + +### Constant symbols + +CC := $(CROSS_COMPILE)gcc +AR := $(CROSS_COMPILE)ar + +CFLAGS=-O2 -Wall -Wextra -std=c99 -Iinc -I. + +OBJDIR = obj + +### Constants for LoRa concentrator HAL library +# List the library sub-modules that are used by the application + +LGW_INC = $(LGW_PATH)/inc/config.h +LGW_INC += $(LGW_PATH)/inc/loragw_hal.h +LGW_INC += $(LGW_PATH)/inc/loragw_aux.h + +### Linking options + +LIBS := -lloragw -lrt -lm + +### General build targets + +all: $(APP_NAME) + +clean: + rm -f $(OBJDIR)/*.o + rm -f $(APP_NAME) + +### HAL library (do no force multiple library rebuild even with 'make -B') + +$(LGW_PATH)/inc/config.h: + @if test ! -f $@; then \ + $(MAKE) all -C $(LGW_PATH); \ + fi + +$(LGW_PATH)/libloragw.a: $(LGW_INC) + @if test ! -f $@; then \ + $(MAKE) all -C $(LGW_PATH); \ + fi + +### Main program compilation and assembly + +$(OBJDIR): + mkdir -p $(OBJDIR) + +$(OBJDIR)/$(APP_NAME).o: src/$(APP_NAME).c $(LGW_INC) | $(OBJDIR) + $(CC) -c $(CFLAGS) -I$(LGW_PATH)/inc $< -o $@ + +$(APP_NAME): $(OBJDIR)/$(APP_NAME).o $(LGW_PATH)/libloragw.a + $(CC) -L$(LGW_PATH) $< -o $@ $(LIBS) + +### EOF diff --git a/util_tx_test/readme.md b/util_tx_test/readme.md new file mode 100644 index 0000000..c46badf --- /dev/null +++ b/util_tx_test/readme.md @@ -0,0 +1,75 @@ + / _____) _ | | + ( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | + (______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +LoRa concentrator packet sender +================================ + +1. Introduction +---------------- + +This software is used to send test packets with a LoRa concentrator. The packets +contain little information, on no protocol (ie. MAC address) information but +can be used to assess the functionality of a gateway downlink using other +gateways as receivers. + +2. Dependencies +---------------- + +This program is a typical example of LoRa concentrator HAL usage for sending +packets. + +Only high-level functions are used (the ones contained in loragw_hal) so there +is no hardware dependencies assuming the HAL is matched with the proper version +of the hardware. +Data structures of the sent packets are accessed by name (ie. not at a +binary level) so new functionalities can be added to the API without affecting +that program at all. + +3. Usage +--------- + +The application runs until the specified number of packets have been sent. +Press Ctrl+C to stop the application before that. + +Use the -h to get help and details about available options. + +The payload content is: +[T][E][S][T][packet counter MSB][packet counter LSB] followed by ASCII padding. + +All LoRa data is scrambled and whitened, so the padding has no influence +whatsoever on the packet error rate. + +4. License +----------- + +Copyright (c) 2013, SEMTECH S.A. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of the Semtech corporation nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*EOF* diff --git a/util_tx_test/src/util_tx_test.c b/util_tx_test/src/util_tx_test.c new file mode 100644 index 0000000..74c163f --- /dev/null +++ b/util_tx_test/src/util_tx_test.c @@ -0,0 +1,661 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Send a bunch of packets on a settable frequency + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +/* fix an issue between POSIX and C99 */ +#if __STDC_VERSION__ >= 199901L + #define _XOPEN_SOURCE 600 +#else + #define _XOPEN_SOURCE 500 +#endif + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ +#include <stdio.h> /* printf fprintf sprintf fopen fputs */ + +#include <string.h> /* memset */ +#include <signal.h> /* sigaction */ +#include <unistd.h> /* getopt access */ +#include <stdlib.h> /* exit codes */ +#include <getopt.h> /* getopt_long */ + +#include "loragw_hal.h" +#include "loragw_reg.h" +#include "loragw_aux.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#define MSG(args...) fprintf(stderr, args) /* message that is destined to the user */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +#define TX_RF_CHAIN 0 /* TX only supported on radio A */ +#define DEFAULT_RSSI_OFFSET 0.0 +#define DEFAULT_MODULATION "LORA" +#define DEFAULT_BR_KBPS 50 +#define DEFAULT_FDEV_KHZ 25 +#define DEFAULT_NOTCH_FREQ 129000U /* 129 kHz */ +#define DEFAULT_SX127X_RSSI_OFFSET -4 /* dB */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE VARIABLES (GLOBAL) ------------------------------------------- */ + +/* signal handling variables */ +struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */ +static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */ +static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */ + +/* TX gain LUT table */ +static struct lgw_tx_gain_lut_s txgain_lut = { + .size = 5, + .lut[0] = { + .dig_gain = 0, + .pa_gain = 0, + .dac_gain = 3, + .mix_gain = 12, + .rf_power = 0 + }, + .lut[1] = { + .dig_gain = 0, + .pa_gain = 1, + .dac_gain = 3, + .mix_gain = 12, + .rf_power = 10 + }, + .lut[2] = { + .dig_gain = 0, + .pa_gain = 2, + .dac_gain = 3, + .mix_gain = 10, + .rf_power = 14 + }, + .lut[3] = { + .dig_gain = 0, + .pa_gain = 3, + .dac_gain = 3, + .mix_gain = 9, + .rf_power = 20 + }, + .lut[4] = { + .dig_gain = 0, + .pa_gain = 3, + .dac_gain = 3, + .mix_gain = 14, + .rf_power = 27 + }}; + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ + +static void sig_handler(int sigio); + +void usage (void); + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ + +static void sig_handler(int sigio) { + if (sigio == SIGQUIT) { + quit_sig = 1;; + } else if ((sigio == SIGINT) || (sigio == SIGTERM)) { + exit_sig = 1; + } +} + +/* describe command line options */ +void usage(void) { + int i; + + printf("*** Library version information ***\n%s\n\n", lgw_version_info()); + printf("Available options:\n"); + printf(" -h print this help\n"); + printf(" -r <int> radio type (SX1255:1255, SX1257:1257)\n"); + printf(" -n <uint> TX notch filter frequency in kHz [126..250]\n"); + printf(" -f <float> target frequency in MHz\n"); + printf(" -k <uint> concentrator clock source (0:Radio A, 1:Radio B)\n"); + printf(" -m <str> modulation type ['LORA', 'FSK']\n"); + printf(" -b <uint> LoRa bandwidth in kHz [125, 250, 500]\n"); + printf(" -s <uint> LoRa Spreading Factor [7-12]\n"); + printf(" -c <uint> LoRa Coding Rate [1-4]\n"); + printf(" -d <uint> FSK frequency deviation in kHz [1:250]\n"); + printf(" -q <float> FSK bitrate in kbps [0.5:250]\n"); + printf(" -p <int> RF power (dBm) [ "); + for (i = 0; i < txgain_lut.size; i++) { + printf("%ddBm ", txgain_lut.lut[i].rf_power); + } + printf("]\n"); + printf(" -l <uint> LoRa preamble length (symbols)\n"); + printf(" -z <uint> payload size (bytes, <256)\n"); + printf(" -i send packet using inverted modulation polarity\n"); + printf(" -t <uint> pause between packets (ms)\n"); + printf(" -x <int> nb of times the sequence is repeated (-1 loop until stopped)\n"); + printf(" --lbt-freq <float> lbt first channel frequency in MHz\n"); + printf(" --lbt-nbch <uint> lbt number of channels [1..8]\n"); + printf(" --lbt-sctm <uint> lbt scan time in usec to be applied to all channels [128, 5000]\n"); + printf(" --lbt-rssi <int> lbt rssi target in dBm [-128..0]\n"); + printf(" --lbt-rssi-offset <int> rssi offset in dB to be applied to SX127x RSSI [-128..127]\n"); +} + +/* -------------------------------------------------------------------------- */ +/* --- MAIN FUNCTION -------------------------------------------------------- */ + +int main(int argc, char **argv) +{ + int i; + uint8_t status_var; + + /* user entry parameters */ + int xi = 0; + unsigned int xu = 0; + double xd = 0.0; + float xf = 0.0; + char arg_s[64]; + + /* application parameters */ + char mod[64] = DEFAULT_MODULATION; + uint32_t f_target = 0; /* target frequency - invalid default value, has to be specified by user */ + int sf = 10; /* SF10 by default */ + int cr = 1; /* CR1 aka 4/5 by default */ + int bw = 125; /* 125kHz bandwidth by default */ + int pow = 14; /* 14 dBm by default */ + int preamb = 8; /* 8 symbol preamble by default */ + int pl_size = 16; /* 16 bytes payload by default */ + int delay = 1000; /* 1 second between packets by default */ + int repeat = -1; /* by default, repeat until stopped */ + bool invert = false; + float br_kbps = DEFAULT_BR_KBPS; + uint8_t fdev_khz = DEFAULT_FDEV_KHZ; + bool lbt_enable = false; + uint32_t lbt_f_target = 0; + uint32_t lbt_sc_time = 5000; + int8_t lbt_rssi_target_dBm = -80; + int8_t lbt_rssi_offset_dB = DEFAULT_SX127X_RSSI_OFFSET; + uint8_t lbt_nb_channel = 1; + uint32_t sx1301_count_us; + uint32_t tx_notch_freq = DEFAULT_NOTCH_FREQ; + + /* RF configuration (TX fail if RF chain is not enabled) */ + enum lgw_radio_type_e radio_type = LGW_RADIO_TYPE_NONE; + uint8_t clocksource = 1; /* Radio B is source by default */ + struct lgw_conf_board_s boardconf; + struct lgw_conf_lbt_s lbtconf; + struct lgw_conf_rxrf_s rfconf; + + /* allocate memory for packet sending */ + struct lgw_pkt_tx_s txpkt; /* array containing 1 outbound packet + metadata */ + + /* loop variables (also use as counters in the packet payload) */ + uint16_t cycle_count = 0; + + /* Parameter parsing */ + int option_index = 0; + static struct option long_options[] = { + {"lbt-freq", required_argument, 0, 0}, + {"lbt-sctm", required_argument, 0, 0}, + {"lbt-rssi", required_argument, 0, 0}, + {"lbt-nbch", required_argument, 0, 0}, + {"lbt-rssi-offset", required_argument, 0, 0}, + {0, 0, 0, 0} + }; + + /* parse command line options */ + while ((i = getopt_long (argc, argv, "hif:n:m:b:s:c:p:l:z:t:x:r:k:d:q:", long_options, &option_index)) != -1) { + switch (i) { + case 'h': + usage(); + return EXIT_FAILURE; + break; + + case 'f': /* <float> Target frequency in MHz */ + i = sscanf(optarg, "%lf", &xd); + if ((i != 1) || (xd < 30.0) || (xd > 3000.0)) { + MSG("ERROR: invalid TX frequency\n"); + usage(); + return EXIT_FAILURE; + } else { + f_target = (uint32_t)((xd*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */ + } + break; + + case 'n': /* <uint> TX notch filter frequency in kHz */ + i = sscanf(optarg, "%i", &xi); + if ((i != 1) || ((xi < 126) || (xi > 250))) { + MSG("ERROR: invalid TX notch filter frequency\n"); + usage(); + return EXIT_FAILURE; + } else { + tx_notch_freq = xi*1000; + } + break; + + case 'm': /* <str> Modulation type */ + i = sscanf(optarg, "%s", arg_s); + if ((i != 1) || ((strcmp(arg_s,"LORA") != 0) && (strcmp(arg_s,"FSK")))) { + MSG("ERROR: invalid modulation type\n"); + usage(); + return EXIT_FAILURE; + } else { + sprintf(mod, "%s", arg_s); + } + break; + + case 'b': /* <int> Modulation bandwidth in kHz */ + i = sscanf(optarg, "%i", &xi); + if ((i != 1) || ((xi != 125) && (xi != 250) && (xi != 500))) { + MSG("ERROR: invalid LoRa bandwidth\n"); + usage(); + return EXIT_FAILURE; + } else { + bw = xi; + } + break; + + case 's': /* <int> Spreading Factor */ + i = sscanf(optarg, "%i", &xi); + if ((i != 1) || (xi < 7) || (xi > 12)) { + MSG("ERROR: invalid spreading factor\n"); + usage(); + return EXIT_FAILURE; + } else { + sf = xi; + } + break; + + case 'c': /* <int> Coding Rate */ + i = sscanf(optarg, "%i", &xi); + if ((i != 1) || (xi < 1) || (xi > 4)) { + MSG("ERROR: invalid coding rate\n"); + usage(); + return EXIT_FAILURE; + } else { + cr = xi; + } + break; + + case 'p': /* <int> RF power */ + i = sscanf(optarg, "%i", &xi); + if ((i != 1) || (xi < -60) || (xi > 60)) { + MSG("ERROR: invalid RF power\n"); + usage(); + return EXIT_FAILURE; + } else { + pow = xi; + } + break; + + case 'd': /* <uint> FSK frequency deviation */ + i = sscanf(optarg, "%u", &xu); + if ((i != 1) || (xu < 1) || (xu > 250)) { + MSG("ERROR: invalid FSK frequency deviation\n"); + usage(); + return EXIT_FAILURE; + } else { + fdev_khz = (uint8_t)xu; + } + break; + + case 'q': /* <float> FSK bitrate */ + i = sscanf(optarg, "%f", &xf); + if ((i != 1) || (xf < 0.5) || (xf > 250)) { + MSG("ERROR: invalid FSK bitrate\n"); + usage(); + return EXIT_FAILURE; + } else { + br_kbps = xf; + } + break; + + case 'l': /* <uint> preamble length (symbols) */ + i = sscanf(optarg, "%i", &xi); + if ((i != 1) || (xi < 6)) { + MSG("ERROR: preamble length must be >6 symbols \n"); + usage(); + return EXIT_FAILURE; + } else { + preamb = xi; + } + break; + + case 'z': /* <uint> payload length (bytes) */ + i = sscanf(optarg, "%i", &xi); + if ((i != 1) || (xi <= 0)) { + MSG("ERROR: invalid payload size\n"); + usage(); + return EXIT_FAILURE; + } else { + pl_size = xi; + } + break; + + case 't': /* <int> pause between packets (ms) */ + i = sscanf(optarg, "%i", &xi); + if ((i != 1) || (xi < 0)) { + MSG("ERROR: invalid time between packets\n"); + usage(); + return EXIT_FAILURE; + } else { + delay = xi; + } + break; + + case 'x': /* <int> numbers of times the sequence is repeated */ + i = sscanf(optarg, "%i", &xi); + if ((i != 1) || (xi < -1)) { + MSG("ERROR: invalid number of repeats\n"); + usage(); + return EXIT_FAILURE; + } else { + repeat = xi; + } + break; + + case 'r': /* <int> Radio type (1255, 1257) */ + sscanf(optarg, "%i", &xi); + switch (xi) { + case 1255: + radio_type = LGW_RADIO_TYPE_SX1255; + break; + case 1257: + radio_type = LGW_RADIO_TYPE_SX1257; + break; + default: + printf("ERROR: invalid radio type\n"); + usage(); + return EXIT_FAILURE; + } + break; + + case 'i': /* Send packet using inverted modulation polarity */ + invert = true; + break; + + case 'k': /* <int> Concentrator clock source (Radio A or Radio B) */ + i = sscanf(optarg, "%i", &xi); + if ((i != 1) || ((xi != 0) && (xi != 1))) { + MSG("ERROR: invalid clock source\n"); + usage(); + return EXIT_FAILURE; + } else { + clocksource = (uint8_t)xi; + } + break; + + case 0: + if( strcmp(long_options[option_index].name, "lbt-freq") == 0 ) { /* <float> LBT first channel frequency in MHz */ + i = sscanf(optarg, "%lf", &xd); + if ((i != 1) || (xd < 30.0) || (xd > 3000.0)) { + MSG("ERROR: invalid LBT start frequency\n"); + usage(); + return EXIT_FAILURE; + } else { + lbt_f_target = (uint32_t)((xd*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */ + lbt_enable = true; + } + } else if( strcmp(long_options[option_index].name, "lbt-sctm") == 0 ) { /* <int> LBT scan time in usec */ + if (lbt_enable == true) { + i = sscanf(optarg, "%i", &xi); + if ((i != 1) || (xi < 0)) { + MSG("ERROR: invalid LBT scan time\n"); + usage(); + return EXIT_FAILURE; + } else { + lbt_sc_time = xi; + } + } else { + MSG("ERROR: invalid parameter, LBT start frequency must be set\n"); + usage(); + return EXIT_FAILURE; + } + } else if( strcmp(long_options[option_index].name, "lbt-rssi") == 0 ) { /* <int> LBT RSSI target */ + if (lbt_enable == true) { + i = sscanf(optarg, "%i", &xi); + if ((i != 1) || ((xi < -128) && (xi > 0))) { + MSG("ERROR: invalid LBT RSSI target\n"); + usage(); + return EXIT_FAILURE; + } else { + lbt_rssi_target_dBm = xi; + } + } else { + MSG("ERROR: invalid parameter, LBT start frequency must be set\n"); + usage(); + return EXIT_FAILURE; + } + } else if( strcmp(long_options[option_index].name, "lbt-rssi-offset") == 0 ) { /* <int> LBT RSSI offset */ + if (lbt_enable == true) { + i = sscanf(optarg, "%i", &xi); + if ((i != 1) || ((xi < -128) && (xi > 127))) { + MSG("ERROR: invalid LBT RSSI offset\n"); + usage(); + return EXIT_FAILURE; + } else { + lbt_rssi_offset_dB = xi; + } + } else { + MSG("ERROR: invalid parameter, LBT start frequency must be set\n"); + usage(); + return EXIT_FAILURE; + } + } else if( strcmp(long_options[option_index].name, "lbt-nbch") == 0 ) { /* <int> LBT number of channels */ + if (lbt_enable == true) { + i = sscanf(optarg, "%i", &xi); + if ((i != 1) || (xi < 0)) { + MSG("ERROR: invalid LBT number of channels\n"); + usage(); + return EXIT_FAILURE; + } else { + lbt_nb_channel = xi; + } + } else { + MSG("ERROR: invalid parameter, LBT start frequency must be set\n"); + usage(); + return EXIT_FAILURE; + } + } + break; + default: + MSG("ERROR: argument parsing\n"); + usage(); + return EXIT_FAILURE; + } + } + + /* check parameter sanity */ + if (f_target == 0) { + MSG("ERROR: frequency parameter not set, please use -f option to specify it.\n"); + return EXIT_FAILURE; + } + if (radio_type == LGW_RADIO_TYPE_NONE) { + MSG("ERROR: radio type parameter not properly set, please use -r option to specify it.\n"); + return EXIT_FAILURE; + } + + /* Summary of packet parameters */ + if (strcmp(mod, "FSK") == 0) { + printf("Sending %i FSK packets on %u Hz (FDev %u kHz, Bitrate %.2f, %i bytes payload, %i symbols preamble) at %i dBm, with %i ms between each\n", repeat, f_target, fdev_khz, br_kbps, pl_size, preamb, pow, delay); + } else { + printf("Sending %i LoRa packets on %u Hz (BW %i kHz, SF %i, CR %i, %i bytes payload, %i symbols preamble) at %i dBm, with %i ms between each\n", repeat, f_target, bw, sf, cr, pl_size, preamb, pow, delay); + } + + /* configure signal handling */ + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_handler = sig_handler; + sigaction(SIGQUIT, &sigact, NULL); + sigaction(SIGINT, &sigact, NULL); + sigaction(SIGTERM, &sigact, NULL); + + /* starting the concentrator */ + /* board config */ + memset(&boardconf, 0, sizeof(boardconf)); + boardconf.lorawan_public = true; + boardconf.clksrc = clocksource; + lgw_board_setconf(boardconf); + + /* LBT config */ + if (lbt_enable) { + memset(&lbtconf, 0, sizeof(lbtconf)); + lbtconf.enable = true; + lbtconf.nb_channel = lbt_nb_channel; + lbtconf.rssi_target = lbt_rssi_target_dBm; + lbtconf.rssi_offset = lbt_rssi_offset_dB; + lbtconf.channels[0].freq_hz = lbt_f_target; + lbtconf.channels[0].scan_time_us = lbt_sc_time; + for (i=1; i<lbt_nb_channel; i++) { + lbtconf.channels[i].freq_hz = lbtconf.channels[i-1].freq_hz + 200E3; /* 200kHz offset for all channels */ + lbtconf.channels[i].scan_time_us = lbt_sc_time; + } + lgw_lbt_setconf(lbtconf); + } + + /* RF config */ + memset(&rfconf, 0, sizeof(rfconf)); + rfconf.enable = true; + rfconf.freq_hz = f_target; + rfconf.rssi_offset = DEFAULT_RSSI_OFFSET; + rfconf.type = radio_type; + for (i = 0; i < LGW_RF_CHAIN_NB; i++) { + if (i == TX_RF_CHAIN) { + rfconf.tx_enable = true; + rfconf.tx_notch_freq = tx_notch_freq; + } else { + rfconf.tx_enable = false; + } + lgw_rxrf_setconf(i, rfconf); + } + + /* TX gain config */ + lgw_txgain_setconf(&txgain_lut); + + /* Start concentrator */ + i = lgw_start(); + if (i == LGW_HAL_SUCCESS) { + MSG("INFO: concentrator started, packet can be sent\n"); + } else { + MSG("ERROR: failed to start the concentrator\n"); + return EXIT_FAILURE; + } + + /* fill-up payload and parameters */ + memset(&txpkt, 0, sizeof(txpkt)); + txpkt.freq_hz = f_target; + if (lbt_enable == true) { + txpkt.tx_mode = TIMESTAMPED; + } else { + txpkt.tx_mode = IMMEDIATE; + } + txpkt.rf_chain = TX_RF_CHAIN; + txpkt.rf_power = pow; + if( strcmp( mod, "FSK" ) == 0 ) { + txpkt.modulation = MOD_FSK; + txpkt.datarate = br_kbps * 1e3; + txpkt.f_dev = fdev_khz; + } else { + txpkt.modulation = MOD_LORA; + switch (bw) { + case 125: txpkt.bandwidth = BW_125KHZ; break; + case 250: txpkt.bandwidth = BW_250KHZ; break; + case 500: txpkt.bandwidth = BW_500KHZ; break; + default: + MSG("ERROR: invalid 'bw' variable\n"); + return EXIT_FAILURE; + } + switch (sf) { + case 7: txpkt.datarate = DR_LORA_SF7; break; + case 8: txpkt.datarate = DR_LORA_SF8; break; + case 9: txpkt.datarate = DR_LORA_SF9; break; + case 10: txpkt.datarate = DR_LORA_SF10; break; + case 11: txpkt.datarate = DR_LORA_SF11; break; + case 12: txpkt.datarate = DR_LORA_SF12; break; + default: + MSG("ERROR: invalid 'sf' variable\n"); + return EXIT_FAILURE; + } + switch (cr) { + case 1: txpkt.coderate = CR_LORA_4_5; break; + case 2: txpkt.coderate = CR_LORA_4_6; break; + case 3: txpkt.coderate = CR_LORA_4_7; break; + case 4: txpkt.coderate = CR_LORA_4_8; break; + default: + MSG("ERROR: invalid 'cr' variable\n"); + return EXIT_FAILURE; + } + } + txpkt.invert_pol = invert; + txpkt.preamble = preamb; + txpkt.size = pl_size; + strcpy((char *)txpkt.payload, "TEST**abcdefghijklmnopqrstuvwxyz#0123456789#ABCDEFGHIJKLMNOPQRSTUVWXYZ#0123456789#abcdefghijklmnopqrstuvwxyz#0123456789#ABCDEFGHIJKLMNOPQRSTUVWXYZ#0123456789#abcdefghijklmnopqrstuvwxyz#0123456789#ABCDEFGHIJKLMNOPQRSTUVWXYZ#0123456789#abcdefghijklmnopqrs#" ); /* abc.. is for padding */ + + /* main loop */ + cycle_count = 0; + while ((repeat == -1) || (cycle_count < repeat)) { + ++cycle_count; + + /* refresh counters in payload (big endian, for readability) */ + txpkt.payload[4] = (uint8_t)(cycle_count >> 8); /* MSB */ + txpkt.payload[5] = (uint8_t)(cycle_count & 0x00FF); /* LSB */ + + /* When LBT is enabled, immediate send is not allowed, so we need + to set a timestamp to the packet */ + if (lbt_enable == true) { + /* Get the current SX1301 time */ + lgw_reg_w(LGW_GPS_EN, 0); + lgw_get_trigcnt(&sx1301_count_us); + lgw_reg_w(LGW_GPS_EN, 1); + + /* Set packet timestamp to current time + few milliseconds */ + txpkt.count_us = sx1301_count_us + 50E3; + } + + /* send packet */ + printf("Sending packet number %u ...", cycle_count); + i = lgw_send(txpkt); /* non-blocking scheduling of TX packet */ + if (i == LGW_HAL_ERROR) { + printf("ERROR\n"); + return EXIT_FAILURE; + } else if (i == LGW_LBT_ISSUE ) { + printf("Failed: Not allowed (LBT)\n"); + } else { + /* wait for packet to finish sending */ + do { + wait_ms(5); + lgw_status(TX_STATUS, &status_var); /* get TX status */ + } while (status_var != TX_FREE); + printf("OK\n"); + } + + /* wait inter-packet delay */ + wait_ms(delay); + + /* exit loop on user signals */ + if ((quit_sig == 1) || (exit_sig == 1)) { + break; + } + } + + /* clean up before leaving */ + lgw_stop(); + + printf("Exiting LoRa concentrator TX test program\n"); + return EXIT_SUCCESS; +} + +/* --- EOF ------------------------------------------------------------------ */ |