From 100555c68c9d73bd41871ac8d1e36cbe4b76f6de Mon Sep 17 00:00:00 2001
From: Luca Erbetta <luca.erbetta@skywarder.eu>
Date: Mon, 26 Apr 2021 21:39:14 +0000
Subject: [PATCH] [Xbee] Driver Rework & GUI to perform antenna test
---
.gitmodules | 3 +
Makefile.template | 12 +-
libs/miosix-kernel | 2 +-
libs/mxgui | 1 +
sbs | 83 +-
sbs.conf | 129 +-
src/entrypoints/examples/mxgui-helloworld.cpp | 44 +
src/entrypoints/kernel-testsuite.cpp | 40 +-
src/shared/ActiveObject.h | 10 +-
src/shared/drivers/BusTemplate.h | 2 +-
src/shared/drivers/Transceiver.h | 2 -
src/shared/drivers/Xbee/APIFrameParser.cpp | 110 +
src/shared/drivers/Xbee/APIFrameParser.h | 86 +
src/shared/drivers/Xbee/APIFrames.h | 475 +
src/shared/drivers/Xbee/APIFramesLog.h | 394 +
src/shared/drivers/Xbee/ATCommands.h | 155 +
src/shared/drivers/Xbee/Xbee.cpp | 519 +-
src/shared/drivers/Xbee/Xbee.h | 790 +-
src/shared/drivers/Xbee/XbeeStatus.h | 64 +-
src/shared/drivers/gps/piksi/piksi.cpp | 5 +-
src/shared/drivers/servo/servo.cpp | 2 +-
src/shared/drivers/spi/SPIBus.h | 85 +-
src/shared/drivers/spi/SPIBusInterface.h | 79 +-
src/shared/drivers/spi/SPITransaction.cpp | 16 +-
src/shared/drivers/spi/SPITransaction.h | 4 +-
.../spi/{SPIBus.cpp => SyncedSPIBus.h} | 47 +-
.../drivers/spi/test}/FakeSpiTypedef.h | 20 +-
.../drivers/spi/{ => test}/MockSPIBus.h | 215 +-
src/shared/logger/Deserializer.h | 2 +-
src/shared/logger/Logger.cpp | 8 +-
src/shared/logger/Logger.h | 17 +-
src/shared/math/Stats.cpp | 4 +-
.../sensors/MS580301BA07/MS580301BA07.h | 2 +-
src/shared/utils/gui/GridLayout.h | 209 +
src/shared/utils/gui/ImageView.h | 58 +
src/shared/utils/gui/Misc.h | 40 +
src/shared/utils/gui/NavController.h | 148 +
src/shared/utils/gui/OptionView.h | 170 +
src/shared/utils/gui/ScreenManager.h | 125 +
src/shared/utils/gui/TextView.h | 181 +
src/shared/utils/gui/VerticalLayout.h | 124 +
src/shared/utils/gui/View.h | 261 +
src/shared/utils/testutils/MockGpioPin.h | 51 +
.../utils/testutils/ThroughputCalculator.h | 176 +
src/tests/catch/spidriver/FakeSPIBus.h | 266 -
src/tests/catch/spidriver/test-spidriver.cpp | 197 +-
src/tests/catch/xbee/MockXbeeSPIBus.h | 172 +
src/tests/catch/xbee/test-xbee-driver.cpp | 415 +
src/tests/catch/xbee/test-xbee-parser.cpp | 575 +
src/tests/drivers/test-mavlink.cpp | 6 +
src/tests/drivers/xbee/EnergyScanData.h | 70 +
src/tests/drivers/xbee/Mark.h | 47 +
src/tests/drivers/xbee/XbeeTestData.h | 153 +
src/tests/drivers/xbee/XbeeTransceiver.h | 424 +
src/tests/drivers/xbee/gui/ConfigScreen.h | 224 +
src/tests/drivers/xbee/gui/EndScreen.h | 58 +
src/tests/drivers/xbee/gui/EnergyScanScreen.h | 232 +
src/tests/drivers/xbee/gui/RespectScreen.h | 40 +
src/tests/drivers/xbee/gui/StatusScreen.h | 334 +
src/tests/drivers/xbee/gui/XbeeGui.h | 74 +
src/tests/drivers/xbee/gui/res/respect.cpp | 9614 +++++++++++++++++
src/tests/drivers/xbee/gui/res/respect.h | 11 +
src/tests/drivers/xbee/logdecoder/.gitignore | 1 +
src/tests/drivers/xbee/logdecoder/LogTypes.h | 68 +
src/tests/drivers/xbee/logdecoder/Makefile | 13 +
.../drivers/xbee/logdecoder/logdecoder.cpp | 168 +
src/tests/drivers/xbee/test-xbee-bidir.cpp | 267 +
src/tests/drivers/xbee/test-xbee-gui.cpp | 368 +
src/tests/drivers/xbee/test-xbee-rcv.cpp | 26 +
src/tests/drivers/xbee/test-xbee-snd.cpp | 26 +
src/tests/test-hsm.cpp | 2 +-
71 files changed, 17622 insertions(+), 1199 deletions(-)
create mode 160000 libs/mxgui
create mode 100644 src/entrypoints/examples/mxgui-helloworld.cpp
create mode 100644 src/shared/drivers/Xbee/APIFrameParser.cpp
create mode 100644 src/shared/drivers/Xbee/APIFrameParser.h
create mode 100644 src/shared/drivers/Xbee/APIFrames.h
create mode 100644 src/shared/drivers/Xbee/APIFramesLog.h
create mode 100644 src/shared/drivers/Xbee/ATCommands.h
rename src/shared/drivers/spi/{SPIBus.cpp => SyncedSPIBus.h} (53%)
rename src/{tests/catch/spidriver => shared/drivers/spi/test}/FakeSpiTypedef.h (89%)
rename src/shared/drivers/spi/{ => test}/MockSPIBus.h (52%)
create mode 100644 src/shared/utils/gui/GridLayout.h
create mode 100644 src/shared/utils/gui/ImageView.h
create mode 100644 src/shared/utils/gui/Misc.h
create mode 100644 src/shared/utils/gui/NavController.h
create mode 100644 src/shared/utils/gui/OptionView.h
create mode 100644 src/shared/utils/gui/ScreenManager.h
create mode 100644 src/shared/utils/gui/TextView.h
create mode 100644 src/shared/utils/gui/VerticalLayout.h
create mode 100644 src/shared/utils/gui/View.h
create mode 100644 src/shared/utils/testutils/MockGpioPin.h
create mode 100644 src/shared/utils/testutils/ThroughputCalculator.h
delete mode 100644 src/tests/catch/spidriver/FakeSPIBus.h
create mode 100644 src/tests/catch/xbee/MockXbeeSPIBus.h
create mode 100644 src/tests/catch/xbee/test-xbee-driver.cpp
create mode 100644 src/tests/catch/xbee/test-xbee-parser.cpp
create mode 100644 src/tests/drivers/xbee/EnergyScanData.h
create mode 100644 src/tests/drivers/xbee/Mark.h
create mode 100644 src/tests/drivers/xbee/XbeeTestData.h
create mode 100644 src/tests/drivers/xbee/XbeeTransceiver.h
create mode 100644 src/tests/drivers/xbee/gui/ConfigScreen.h
create mode 100644 src/tests/drivers/xbee/gui/EndScreen.h
create mode 100644 src/tests/drivers/xbee/gui/EnergyScanScreen.h
create mode 100644 src/tests/drivers/xbee/gui/RespectScreen.h
create mode 100644 src/tests/drivers/xbee/gui/StatusScreen.h
create mode 100644 src/tests/drivers/xbee/gui/XbeeGui.h
create mode 100644 src/tests/drivers/xbee/gui/res/respect.cpp
create mode 100644 src/tests/drivers/xbee/gui/res/respect.h
create mode 100644 src/tests/drivers/xbee/logdecoder/.gitignore
create mode 100644 src/tests/drivers/xbee/logdecoder/LogTypes.h
create mode 100644 src/tests/drivers/xbee/logdecoder/Makefile
create mode 100644 src/tests/drivers/xbee/logdecoder/logdecoder.cpp
create mode 100644 src/tests/drivers/xbee/test-xbee-bidir.cpp
create mode 100644 src/tests/drivers/xbee/test-xbee-gui.cpp
create mode 100644 src/tests/drivers/xbee/test-xbee-rcv.cpp
create mode 100644 src/tests/drivers/xbee/test-xbee-snd.cpp
diff --git a/.gitmodules b/.gitmodules
index 452224ad7..6dd7bd532 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -14,3 +14,6 @@
[submodule "libs/eigen"]
path = libs/eigen
url = https://gitlab.com/libeigen/eigen.git
+[submodule "libs/mxgui"]
+ path = libs/mxgui
+ url = git@git.skywarder.eu:scs/mxgui.git
diff --git a/Makefile.template b/Makefile.template
index ca62562a9..3b3ea18e0 100644
--- a/Makefile.template
+++ b/Makefile.template
@@ -15,7 +15,7 @@ include $(CONFPATH)/config/Makefile.inc
##
## List here subdirectories which contains makefiles
##
-SUBDIRS := $(KPATH) {SBS_PROJECT_SUBDIRS}
+SUBDIRS := $(KPATH) {SBS_PROJECT_SUBDIRS} {SBS_LIB_SUBDIR}
##
## List here your source files (both .s, .c and .cpp)
@@ -25,13 +25,13 @@ SRC := {SBS_SOURCE_FILES}
##
## List here additional static libraries with relative path
##
-LIBS := {SBS_PROJECT_LIBS}
+LIBS := {SBS_PROJECT_LIBS} {SBS_LIB_LIBS}
##
## List here additional include directories (in the form -Iinclude_dir)
##
INCLUDE_DIRS := -I$(SBS_BASE)/libs -I$(SBS_BASE)/libs/miosix-kernel/miosix \
- {SBS_PROJECT_INCLUDES}
+ {SBS_PROJECT_INCLUDES} {SBS_LIB_INCLUDES}
##############################################################################
## You should not need to modify anything below ##
@@ -54,10 +54,12 @@ OBJ := $(addprefix $(OBJDIR)/, $(addsuffix .o, $(basename $(SRC))))
## Always include CONFPATH first, as it overrides the config file location
CXXFLAGS := $(CXXFLAGS_BASE) -I$(CONFPATH) -I$(CONFPATH)/config/$(BOARD_INC) \
-I. -I$(KPATH) -I$(KPATH)/arch/common -I$(KPATH)/$(ARCH_INC) \
- -I$(KPATH)/$(BOARD_INC) $(INCLUDE_DIRS) -Wall -Wextra -pedantic
+ -I$(KPATH)/$(BOARD_INC) $(INCLUDE_DIRS) -Wall -Wextra -Wcast-align \
+ -pedantic
CFLAGS := $(CFLAGS_BASE) -I$(CONFPATH) -I$(CONFPATH)/config/$(BOARD_INC) \
-I. -I$(KPATH) -I$(KPATH)/arch/common -I$(KPATH)/$(ARCH_INC) \
- -I$(KPATH)/$(BOARD_INC) $(INCLUDE_DIRS) -Wall -Wextra -pedantic
+ -I$(KPATH)/$(BOARD_INC) $(INCLUDE_DIRS) -Wall -Wextra -Wcast-align \
+ -pedantic
AFLAGS := $(AFLAGS_BASE)
LFLAGS := $(LFLAGS_BASE) -Wl,--gc-sections,-Map,$(BINDIR)/${SBS_BIN_NAME}.map
DFLAGS := -MMD -MP
diff --git a/libs/miosix-kernel b/libs/miosix-kernel
index 7cda1e7c1..7ff90e110 160000
--- a/libs/miosix-kernel
+++ b/libs/miosix-kernel
@@ -1 +1 @@
-Subproject commit 7cda1e7c1d55766f2f75806b56e4bad60db1fcf7
+Subproject commit 7ff90e110b1e0cb0c7acc7c652cf593087ccd18a
diff --git a/libs/mxgui b/libs/mxgui
new file mode 160000
index 000000000..cab17f618
--- /dev/null
+++ b/libs/mxgui
@@ -0,0 +1 @@
+Subproject commit cab17f6185da95502d20dd31b17fef67e06ca6ef
diff --git a/sbs b/sbs
index d159ba3a5..6c5201fe2 100755
--- a/sbs
+++ b/sbs
@@ -48,9 +48,8 @@ except ImportError:
# Global variables
#
srcfiles={}
+extlibs={}
mainfiles={}
-tests={}
-entrypoints={}
colors=False
# Map for the project configuration, filled in when parsing sbs.conf
@@ -154,27 +153,30 @@ def parseConf(path):
projconf[i] = conf.get('SBS_PROJECT', i)
elif stype == 'srcfiles':
srcfiles[i] = [x.strip() for x in conf.get(i, 'Files').split("\n")]
- elif stype == 'board':
- entrypoints[i] = { 'id': conf.get(i, 'BoardId'),
- 'bin': conf.get(i, 'BinName'),
- 'defines': conf.get(i, 'Defines'),
- 'files': [entrypoint_mask % conf.get(i, 'Main').strip()] +
- [x.strip() for x in conf.get(i, 'Include').split(' ')]
- }
- elif stype == 'test':
- tests[i] = { 'id': conf.get(i, 'BoardId'),
+ elif stype == 'library':
+ extlibs[i] = {'subdir': conf.get(i, 'Subdir'),
+ 'libs': [x.strip() for x in conf.get(i, 'Libs').split()],
+ 'includes': [x.strip() for x in conf.get(i, 'Includes').split()]}
+ elif stype == 'board' or stype == 'test':
+ if stype == 'test':
+ mask = test_mask
+ add_def = " -I" + projconf['TESTS_PATH']
+ else:
+ mask = entrypoint_mask
+ add_def = ""
+
+ mainfiles[i] = {'type': stype,
+ 'id': conf.get(i, 'BoardId').strip(),
'bin': conf.get(i, 'BinName'),
- 'defines': conf.get(i, 'Defines') + " -I" + projconf['TESTS_PATH'],
- 'files': [test_mask % conf.get(i, 'Main').strip()] +
+ 'defines': conf.get(i, 'Defines') + add_def,
+ 'libs': [x.strip() for x in conf.get(i, 'Libs', fallback="").split()],
+ 'files': [mask % conf.get(i, 'Main').strip()] +
[x.strip() for x in conf.get(i, 'Include').split(' ')]
}
else:
print('[SBS] Syntax error in %s: type %s not implemented' % (path, stype))
exit(1)
- # Join tests and entrypoints in mainfiles
- mainfiles.update(entrypoints)
- mainfiles.update(tests)
# Exit if nothing was found
if (len(mainfiles) == 0 ):
@@ -184,6 +186,7 @@ def parseConf(path):
# Import also srcfiles from boardcore if executing from another folders
if projconf['SBS_BASE'] != '.':
importSrcFiles(projconf['SBS_BASE'] + "/sbs.conf")
+ importLibs(projconf['SBS_BASE'] + "/sbs.conf")
# Substitute includes
for i in mainfiles:
@@ -194,6 +197,13 @@ def parseConf(path):
else:
files += [j]
mainfiles[i]['files'] = files
+
+ # Substitute libs
+ for i in mainfiles:
+ lib_dicts = {}
+ for j in mainfiles[i]['libs']:
+ lib_dicts[j] = extlibs[j]
+ mainfiles[i]['libs'] = lib_dicts
#
# Create a Makefile for a specific board starting from Makefile.template
@@ -208,7 +218,10 @@ def build_makefile(template, board, bname):
"MAP_FILE": "export MAIN_MAP_FILE := bin/%s.map\n" % board['bin'],
"SOURCE_FILES": "%s\n" % (" ".join(board['files'])),
"CUSTOM_DEFINES": board['defines'],
- "BIN_NAME": board['bin']
+ "BIN_NAME": board['bin'],
+ "LIB_SUBDIR": " ".join([x['subdir'] for _,x in board['libs'].items()]),
+ "LIB_LIBS": " ".join([" ".join(x['libs']) for _,x in board['libs'].items()]),
+ "LIB_INCLUDES": " ".join([" ".join([ "-I" + s for s in x['includes']]) for _,x in board['libs'].items()])
}
rmap["PROJECT_INCLUDES"] = "-I" + projconf["SRC_PATH"].strip() + " "
@@ -255,7 +268,24 @@ def importSrcFiles(path):
else:
print('[SBS] Syntax error in %s: a srcfiles group named %s is already defined. Terminating' % (path, i))
exit(1)
+#
+# Import libraries groups from another .conf file.
+# Libraries are added only if there's not a group already with the same name.
+#
+def importLibs(path):
+ conf = cp.RawConfigParser()
+ conf.read(path)
+ for i in conf.sections():
+ stype = conf.get(i, 'Type')
+ if stype == 'library':
+ if i not in extlibs:
+ extlibs[i] = {'subdir': conf.get(i, 'Subdir'),
+ 'libs': [x.strip() for x in conf.get(i, 'Libs').split()],
+ 'includes': [x.strip() for x in conf.get(i, 'Includes').split()]}
+ else:
+ print('[SBS] Syntax error in %s: a library group named %s is already defined. Terminating' % (path, i))
+ exit(1)
##-------------------------------------------------------------
## MAIN
@@ -297,14 +327,17 @@ if options.lint == True:
# List
if options.list == True:
print('[SBS] List of available entrypoints:')
- for entrypoint in entrypoints:
- print(entrypoint)
+ for main in mainfiles:
+ if(mainfiles[main]['type'] == 'board'):
+ print(main)
print('\n[SBS] List of available tests:')
- for test in tests:
- print(test)
+ for main in mainfiles:
+ if(mainfiles[main]['type'] == 'test'):
+ print(main)
exit(0)
# Set how many main files to build:
+files = list(mainfiles.keys()).copy()
if options.board != None: # just one
try:
good = mainfiles[options.board]
@@ -313,9 +346,13 @@ if options.board != None: # just one
print('[SBS] No entrypoint or test named %s. Terminating.' % (options.board))
sys.exit(2)
elif options.all_entry == True: # all entrypoints
- mainfiles = entrypoints
+ for main in files:
+ if(mainfiles[main]['type'] == 'test'):
+ del mainfiles[main]
elif options.all_tests == True: # all tests
- mainfiles = tests
+ for main in files:
+ if(mainfiles[main]['type'] == 'board'):
+ del mainfiles[main]
# Set action
diff --git a/sbs.conf b/sbs.conf
index e05829a53..a02fab8cf 100644
--- a/sbs.conf
+++ b/sbs.conf
@@ -77,16 +77,36 @@ ENTRY_PATH: src/entrypoints
TESTS_PATH: src/tests
SRC_PATH: src/shared
SBS_BASE: .
-PROJECT_INCLUDES: -Ilibs/eigen/
-PROJECT_SUBDIRS:
-PROJECT_LIBS:
+PROJECT_INCLUDES:
+PROJECT_SUBDIRS:
+PROJECT_LIBS:
+
+#----------------------------------------------#
+# List of External Libraries #
+#----------------------------------------------#
+# [name] -> can be included using 'name'
+# Type: library
+# Subdir: Subdirectory containing the library makefile
+# Libs: Static libraries to link against (whitespace-separated)
+# Includes: List of additional include directories (whitespace-separated)
+[libmxgui]
+Type: library
+Subdir: libs/mxgui
+Libs: libs/mxgui/libmxgui.a
+Includes: libs/mxgui
+
+[eigen]
+Type: library
+Subdir:
+Libs:
+Includes: libs/eigen
#-----------------------------------#
# List of Sources #
#-----------------------------------#
# [name] -> can be included using '%name'
# Type: srcfiles
-# Files: a '/n'-separated list of f
+# Files: a '/n'-separated list of source files
[shared]
Type: srcfiles
@@ -121,8 +141,7 @@ Files: src/shared/drivers/pwm/pwm.cpp
[spi]
Type: srcfiles
-Files: src/shared/drivers/spi/SPIBus.cpp
- src/shared/drivers/spi/SPITransaction.cpp
+Files: src/shared/drivers/spi/SPITransaction.cpp
[i2c]
Type: srcfiles
@@ -163,7 +182,9 @@ Files: src/shared/drivers/gamma868/Gamma868.cpp
[xbee]
Type: srcfiles
-Files: src/shared/drivers/Xbee/Xbee.cpp
+Files: src/shared/drivers/Xbee/APIFrameParser.cpp
+ src/shared/drivers/Xbee/Xbee.cpp
+
[events]
Type: srcfiles
@@ -192,6 +213,8 @@ Files: src/tests/catch/test-aero.cpp
src/tests/catch/test-matrix.cpp
src/tests/catch/test-packetqueue.cpp
src/tests/catch/spidriver/test-spidriver
+ src/tests/catch/xbee/test-xbee-parser.cpp
+ src/tests/catch/xbee/test-xbee-driver.cpp
[internal-adc]
Type: srcfiles
@@ -231,6 +254,15 @@ Include:
Defines:
Main: kernel-testsuite
+[mxgui-helloworld]
+Type: board
+BoardId: stm32f429zi_stm32f4discovery
+BinName: mxgui-helloworld
+Include:
+Defines: -DDEBUG
+Libs: libmxgui
+Main: examples/mxgui-helloworld
+
[sdcard-benchmark]
Type: board
BoardId: stm32f429zi_stm32f4discovery
@@ -256,8 +288,8 @@ Main: sdcard-benchmark
Type: test
BoardId: stm32f429zi_stm32f4discovery
BinName: tests-catch
-Include: %tests-boardcore %shared %test-utils %spi %sensormanager
-Defines:
+Include: %tests-boardcore %shared %test-utils %spi %sensormanager %xbee
+Defines: -DUSE_MOCK_PERIPHERALS
Main: catch/catch-tests-entry
[test-kalman]
@@ -274,6 +306,7 @@ BoardId: stm32f429zi_stm32f4discovery
BinName: test-kalman-eigen
Include: %shared
Defines: -DDEBUG -DSTANDALONE_CATCH1_TEST
+Libs: eigen
Main: catch/test-kalman-eigen
[test-taskscheduler]
@@ -338,6 +371,7 @@ BoardId: stm32f429zi_stm32f4discovery
BinName: test-kalman-eigen-benchmark
Include: %shared
Defines:
+Libs: eigen
Main: kalman/test-kalman-eigen-benchmark
[test-pinobserver]
@@ -414,29 +448,29 @@ Include: %shared %gamma868
Defines: -DDEBUG
Main: drivers/test-mavlink
-[xbee-bitrate]
-Type: test
-BoardId: stm32f429zi_skyward_death_stack
-BinName: xbee-bitrate
-Include: %shared %xbee %spi
-Defines: -DDEBUG -DSDRAM_ISSI
-Main: misc/xbee-bitrate
+# [xbee-bitrate]
+# Type: test
+# BoardId: stm32f429zi_skyward_death_stack
+# BinName: xbee-bitrate
+# Include: %shared %xbee %spi
+# Defines: -DDEBUG -DSDRAM_ISSI
+# Main: misc/xbee-bitrate
-[xbee-send-rcv]
-Type: test
-BoardId: stm32f429zi_skyward_death_stack
-BinName: xbee-send-rcv
-Include: %shared %xbee %spi
-Defines: -DDEBUG
-Main: misc/xbee-send-rcv
+# [xbee-send-rcv]
+# Type: test
+# BoardId: stm32f429zi_skyward_death_stack
+# BinName: xbee-send-rcv
+# Include: %shared %xbee %spi
+# Defines: -DDEBUG
+# Main: misc/xbee-send-rcv
-[xbee-time-to-send]
-Type: test
-BoardId: stm32f429zi_skyward_death_stack
-BinName: xbee-time-to-send
-Include: %shared %xbee %spi
-Defines: -DDEBUG
-Main: misc/xbee-time-to-send
+# [xbee-time-to-send]
+# Type: test
+# BoardId: stm32f429zi_skyward_death_stack
+# BinName: xbee-time-to-send
+# Include: %shared %xbee %spi
+# Defines: -DDEBUG
+# Main: misc/xbee-time-to-send
[test-circularbuffer]
Type: test
@@ -534,6 +568,39 @@ Include: %shared %spi %ads1118
Defines: -DDEBUG
Main: drivers/test-ads1118
+[test-xbee-snd]
+Type: test
+BoardId: stm32f429zi_stm32f4discovery
+BinName: test-xbee-snd
+Include: %shared %spi %xbee %logger
+Defines: -DDEBUG
+Main: drivers/xbee/test-xbee-snd
+
+[test-xbee-rcv]
+Type: test
+BoardId: stm32f429zi_stm32f4discovery
+BinName: test-xbee-rcv
+Include: %shared %spi %xbee %logger
+Defines: -DDEBUG
+Main: drivers/xbee/test-xbee-rcv
+
+[test-xbee-bidir]
+Type: test
+BoardId: stm32f429zi_stm32f4discovery
+BinName: test-xbee-bidir
+Include: %shared %spi %xbee %logger
+Defines: -DDEBUG
+Main: drivers/xbee/test-xbee-bidir
+
+[test-xbee-gui]
+Type: test
+BoardId: stm32f429zi_stm32f4discovery
+BinName: test-xbee-gui
+Include: %shared %spi %xbee %logger src/tests/drivers/xbee/gui/res/respect.cpp
+Defines: -DDEBUG
+Libs: libmxgui
+Main: drivers/xbee/test-xbee-gui
+
[test-bme280]
Type: test
BoardId: stm32f407vg_stm32f4discovery
@@ -686,4 +753,4 @@ Main: drivers/test-hwtimer-chain
# BinName: calibrate-mpu9250
# Include: %shared %sensormanager
# Defines:
-# Main: drivers/calibrate-mpu9250
\ No newline at end of file
+# Main: drivers/calibrate-mpu9250
diff --git a/src/entrypoints/examples/mxgui-helloworld.cpp b/src/entrypoints/examples/mxgui-helloworld.cpp
new file mode 100644
index 000000000..f2ef202d8
--- /dev/null
+++ b/src/entrypoints/examples/mxgui-helloworld.cpp
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#include <cstdio>
+#include <miosix.h>
+
+#include <mxgui/entry.h>
+#include <mxgui/display.h>
+
+using namespace mxgui;
+using namespace miosix;
+
+int main()
+{
+ {
+ DrawingContext dc(DisplayManager::instance().getDisplay());
+ dc.write(Point(0,0),"Hello world");
+ }
+ for(;;)
+ {
+ printf("Hello world! Look at the screen!\n");
+ Thread::sleep(1000);
+ }
+}
\ No newline at end of file
diff --git a/src/entrypoints/kernel-testsuite.cpp b/src/entrypoints/kernel-testsuite.cpp
index ce71b9421..dd00dac1a 100644
--- a/src/entrypoints/kernel-testsuite.cpp
+++ b/src/entrypoints/kernel-testsuite.cpp
@@ -190,6 +190,7 @@ int main()
}
//For testing mpu
unsigned int *m;
+ (void)m; // Disable unused warning
switch(c)
{
case 't':
@@ -347,6 +348,7 @@ int main()
case 's':
iprintf("Shutting down\n");
shutdown();
+ break;
default:
iprintf("Unrecognized option\n");
}
@@ -609,6 +611,7 @@ static volatile bool t1_v1;
static void t1_p1(void *argv)
{
+ (void)argv;
for(;;)
{
if(Thread::testTerminate()) break;
@@ -701,6 +704,8 @@ static Thread *t2_p_v1;
static void t2_p1(void *argv)
{
+ (void)argv;
+
//This is to fix a race condition: the immediately after the thread
//creation a yield occurs, t2_p_v1 is not yet assigned so the check fails
Thread::sleep(5);
@@ -718,6 +723,7 @@ static void t2_p1(void *argv)
static void t2_p2(void *argv)
{
+ (void)argv;
while(Thread::testTerminate()==false) t2_v1=true;
}
@@ -782,6 +788,7 @@ also tests creation of multiple instances of the same thread
static void t3_p1(void *argv)
{
+ (void)argv;
const int SLEEP_TIME=100;
for(;;)
{
@@ -799,6 +806,7 @@ static volatile bool t3_deleted;//Set when an instance of t3_p2 is deleted
static void t3_p2(void *argv)
{
+ (void)argv;
const int SLEEP_TIME=15;
for(;;)
{
@@ -893,6 +901,7 @@ static volatile bool t4_v1;
static void t4_p1(void *argv)
{
+ (void)argv;
for(;;)
{
if(Thread::testTerminate()) break;
@@ -1035,6 +1044,7 @@ static volatile bool t5_v2;//False=testing Thread::wait() else Thread::IRQwait()
static void t5_p1(void *argv)
{
+ (void)argv;
for(;;)
{
if(Thread::testTerminate()) break;
@@ -1153,6 +1163,7 @@ static FastMutex t6_m1a;
static void t6_p1(void *argv)
{
+ (void)argv;
t6_m1.lock();
seq.add('1');
Thread::sleep(100);
@@ -1165,6 +1176,7 @@ static void t6_p1(void *argv)
static void t6_p2(void *argv)
{
+ (void)argv;
t6_m1.lock();
seq.add('2');
Thread::sleep(100);
@@ -1176,6 +1188,7 @@ static void t6_p2(void *argv)
static void t6_p3(void *argv)
{
+ (void)argv;
t6_m1.lock();
seq.add('3');
Thread::sleep(100);
@@ -1187,6 +1200,7 @@ static void t6_p3(void *argv)
static void t6_p4(void *argv)
{
+ (void)argv;
t6_m1.lock();
for(;;)
{
@@ -1205,6 +1219,7 @@ static void t6_p4(void *argv)
static void t6_p1a(void *argv)
{
+ (void)argv;
t6_m1a.lock();
seq.add('1');
Thread::sleep(100);
@@ -1213,6 +1228,7 @@ static void t6_p1a(void *argv)
static void t6_p2a(void *argv)
{
+ (void)argv;
t6_m1a.lock();
seq.add('2');
Thread::sleep(100);
@@ -1221,6 +1237,7 @@ static void t6_p2a(void *argv)
static void t6_p3a(void *argv)
{
+ (void)argv;
t6_m1a.lock();
seq.add('3');
Thread::sleep(100);
@@ -1229,6 +1246,7 @@ static void t6_p3a(void *argv)
static void t6_p4a(void *argv)
{
+ (void)argv;
t6_m1a.lock();
for(;;)
{
@@ -1251,6 +1269,7 @@ static FastMutex t6_m2a;
static void t6_p5(void *argv)
{
+ (void)argv;
for(;;)
{
if(Thread::testTerminate()) break;
@@ -1266,6 +1285,7 @@ static void t6_p5(void *argv)
static void t6_p5a(void *argv)
{
+ (void)argv;
for(;;)
{
if(Thread::testTerminate()) break;
@@ -1305,6 +1325,7 @@ static FastMutex t6_m5a(FastMutex::RECURSIVE);
static void *t6_p7(void *argv)
{
+ (void)argv;
if(t6_m5.tryLock()==false) return reinterpret_cast<void*>(1); // 1 = locked
t6_m5.unlock();
return reinterpret_cast<void*>(0); // 0 = unlocked
@@ -1320,6 +1341,7 @@ bool checkIft6_m5IsLocked()
static void *t6_p7a(void *argv)
{
+ (void)argv;
if(t6_m5a.tryLock()==false) return reinterpret_cast<void*>(1); // 1 = locked
t6_m5a.unlock();
return reinterpret_cast<void*>(0); // 0 = unlocked
@@ -1741,6 +1763,7 @@ static Queue<char,4> t8_q2;
static void t8_p1(void *argv)
{
+ (void)argv;
for(;;)
{
if(Thread::testTerminate()) break;
@@ -1985,6 +2008,7 @@ void t10_f2()
void t10_p1(void *argv)
{
+ (void)argv;
t10_f2();
fail("Exception not thrown");
}
@@ -2011,6 +2035,7 @@ static volatile unsigned int t11_v1;//Free heap after spawning thread
void t11_p1(void *argv)
{
+ (void)argv;
if(MemoryProfiling::getStackSize()!=STACK_SMALL) fail("getStackSize (2)");
//Check that getCurrentFreeHeap returns the same value from different
//threads if no heap allocation happened in between
@@ -2032,7 +2057,8 @@ void test_11()
fail("getAbsoluteFreeStack (1)");
if(MemoryProfiling::getAbsoluteFreeStack()>curFreeStack-4)
fail("getAbsoluteFreeStack (2)");
- unsigned int heapSize=MemoryProfiling::getHeapSize();
+
+ unsigned int heapSize=MemoryProfiling::getHeapSize();
if(MemoryProfiling::getCurrentFreeHeap()>heapSize)
fail("getCurrentFreeHeap");
if(MemoryProfiling::getAbsoluteFreeHeap()>heapSize)
@@ -2057,12 +2083,14 @@ Mutex t12_m2;
void t12_p1(void *argv)
{
+ (void)argv;
Lock<Mutex> l1(t12_m1);
Lock<Mutex> l2(t12_m2);
}
void t12_p2(void *argv)
{
+ (void)argv;
Lock<Mutex> l(t12_m1);
}
@@ -2265,6 +2293,7 @@ static Mutex t15_m1;
void t15_p1(void *argv)
{
+ (void)argv;
for(int i=0;i<10;i++)
{
Lock<Mutex> l(t15_m1);
@@ -2276,6 +2305,7 @@ void t15_p1(void *argv)
void t15_p2(void *argv)
{
+ (void)argv;
for(int i=0;i<10;i++)
{
Lock<Mutex> l(t15_m1);
@@ -2395,6 +2425,7 @@ pthread_cond_t t16_c1=PTHREAD_COND_INITIALIZER;
void *t16_p3(void *argv)
{
+ (void)argv;
Thread::sleep(30);
if(pthread_mutex_trylock(&t16_m1)!=0)
fail("cond_wait did not release mutex"); //<---
@@ -2408,6 +2439,7 @@ pthread_cond_t t16_c2;
void *t16_p4(void *argv)
{
+ (void)argv;
Thread::sleep(30);
if(pthread_mutex_trylock(&t16_m1)!=0)
fail("cond_wait did not release mutex (2)"); //<---
@@ -2791,6 +2823,7 @@ static void be()
static void t19_p1(void *argv)
{
+ (void)argv;
Thread::sleep(50);
{
FastInterruptDisableLock dLock;
@@ -3232,6 +3265,7 @@ struct t22_s1
static void t22_t2(void *argv)
{
+ (void)argv;
while(Thread::testTerminate()==false)
{
t22_v5=true;
@@ -3958,6 +3992,7 @@ static volatile bool fs_1_error;
static void fs_t1_p1(void *argv)
{
+ (void)argv;
FILE *f;
if((f=fopen("/sd/testdir/file_1.txt","w"))==NULL)
{
@@ -3990,6 +4025,7 @@ static void fs_t1_p1(void *argv)
static void fs_t1_p2(void *argv)
{
+ (void)argv;
FILE *f;
if((f=fopen("/sd/testdir/file_2.txt","w"))==NULL)
{
@@ -4022,6 +4058,7 @@ static void fs_t1_p2(void *argv)
static void fs_t1_p3(void *argv)
{
+ (void)argv;
FILE *f;
if((f=fopen("/sd/testdir/file_3.txt","w"))==NULL)
{
@@ -4558,6 +4595,7 @@ context switch speed
static void b2_p1(void *argv)
{
+ (void)argv;
for(;;)
{
if(Thread::testTerminate()) break;
diff --git a/src/shared/ActiveObject.h b/src/shared/ActiveObject.h
index f3cd2a415..9204180b1 100644
--- a/src/shared/ActiveObject.h
+++ b/src/shared/ActiveObject.h
@@ -26,6 +26,7 @@
#define SRC_SHARED_ACTIVEOBJECT_H
#include <Common.h>
+
#include "diagnostic/SkywardStack.h"
/**
@@ -84,9 +85,12 @@ public:
*/
virtual void stop()
{
- should_stop = true;
- thread->join();
- stopped = true;
+ if (isRunning())
+ {
+ should_stop = true;
+ thread->join();
+ stopped = true;
+ }
}
bool isStarted() { return started; }
diff --git a/src/shared/drivers/BusTemplate.h b/src/shared/drivers/BusTemplate.h
index 0a9ebfbc6..da58a217e 100644
--- a/src/shared/drivers/BusTemplate.h
+++ b/src/shared/drivers/BusTemplate.h
@@ -219,7 +219,7 @@ private:
return n_spi == 1 || n_spi == 2 ? 5 : 6;
}
- constexpr SPI_TypeDef* getSPIAddr(unsigned n)
+ constexpr SPI_TypeDef* getSPIAddr(unsigned n) const
{
return n == 1 ? SPI1 : n == 2 ? SPI2 : SPI3;
}
diff --git a/src/shared/drivers/Transceiver.h b/src/shared/drivers/Transceiver.h
index 3bfd631a8..fca353116 100644
--- a/src/shared/drivers/Transceiver.h
+++ b/src/shared/drivers/Transceiver.h
@@ -23,8 +23,6 @@
#ifndef TRANSCEIVER_H
#define TRANSCEIVER_H
-#include <Common.h>
-
class Transceiver
{
public:
diff --git a/src/shared/drivers/Xbee/APIFrameParser.cpp b/src/shared/drivers/Xbee/APIFrameParser.cpp
new file mode 100644
index 000000000..ed91fa946
--- /dev/null
+++ b/src/shared/drivers/Xbee/APIFrameParser.cpp
@@ -0,0 +1,110 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#include "APIFrameParser.h"
+#include "Debug.h"
+
+namespace Xbee
+{
+
+APIFrameParser::APIFrameParser() {}
+
+APIFrameParser::ParseResult APIFrameParser::parse(uint8_t byte, APIFrame* frame)
+{
+ switch (parser_state)
+ {
+ // Look for the start of frame delimiter
+ case ParserState::FIND_START:
+
+ if (byte == START_DELIMITER)
+ {
+ parser_state = ParserState::READ_LENGTH_1;
+ }
+ break;
+ // Read most significant byte of the length
+ case ParserState::READ_LENGTH_1:
+ frame->length = byte;
+ parser_state = ParserState::READ_LENGTH_2;
+
+ break;
+ // Read least significant byte of the length
+ case ParserState::READ_LENGTH_2:
+ frame->length |= ((uint16_t)byte << 8) & 0xFF00;
+ // At least two frame data bytes (frame_type and a payload)
+ if(swapBytes16(frame->length) < 2)
+ {
+ parser_state = ParserState::FIND_START;
+ return ParseResult::FAIL;
+ }
+
+ if (frame->getFrameDataLength() > FRAME_DATA_SIZE)
+ {
+ parser_state = ParserState::FIND_START;
+ return ParseResult::FAIL;
+ }
+
+ parser_state = ParserState::READ_FRAME_TYPE;
+ break;
+ // Read frame type
+ case ParserState::READ_FRAME_TYPE:
+ frame->frame_type = byte;
+ parser_state = ParserState::READ_FRAME_DATA;
+ break;
+ // Read the data frame
+ case ParserState::READ_FRAME_DATA:
+ frame->frame_data[current_frame_data_index++] = byte;
+
+ if (current_frame_data_index == frame->getFrameDataLength())
+ {
+ current_frame_data_index = 0;
+ parser_state = ParserState::READ_CHECKSUM;
+ }
+ break;
+ // Read & verify checksum
+ case ParserState::READ_CHECKSUM:
+ frame->checksum = byte;
+ parser_state = ParserState::FIND_START;
+
+ if (frame->verifyChecksum())
+ {
+ return ParseResult::SUCCESS;
+ }
+ else
+ {
+ TRACE("[XbeeParser] Wrong packet checksum!\n");
+ return ParseResult::FAIL;
+ }
+ break;
+ }
+
+ if (parser_state != ParserState::FIND_START)
+ {
+ return ParseResult::PARSING;
+ }
+ else
+ {
+ return ParseResult::IDLE;
+ }
+}
+
+} // namespace Xbee
\ No newline at end of file
diff --git a/src/shared/drivers/Xbee/APIFrameParser.h b/src/shared/drivers/Xbee/APIFrameParser.h
new file mode 100644
index 000000000..d4dcaaa03
--- /dev/null
+++ b/src/shared/drivers/Xbee/APIFrameParser.h
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include "APIFrames.h"
+
+namespace Xbee
+{
+
+/**
+ * @brief Parses a byte sequence into an Xbee APIFrame
+ */
+class APIFrameParser
+{
+public:
+ /**
+ * @brief Current state of the parser internal state machine
+ */
+ enum class ParserState
+ {
+ FIND_START,
+ READ_LENGTH_1,
+ READ_LENGTH_2,
+ READ_FRAME_TYPE,
+ READ_FRAME_DATA,
+ READ_CHECKSUM
+ };
+
+ /**
+ * @brief Result of the last parse operation
+ *
+ */
+ enum class ParseResult : uint8_t
+ {
+ IDLE = 0, // No frame has been found yet
+ PARSING, // Currently paring a frame
+ SUCCESS, // A frame has been parsed successfully
+ FAIL // The parsed frame was invalid (eg wrong length, wrong checksum)
+ };
+
+ APIFrameParser();
+
+ /**
+ * @brief Parses a single byte. When this function returns
+ * ParseResult:SUCESS, @p frame contains a valid APIFrame
+ *
+ * @param byte Byte to parse
+ * @param frame Frame to be constructed from the parsed data.
+ * @return ParseResult If SUCCESS is returned, then the provided frame is
+ * valid and can be used
+ */
+ ParseResult parse(uint8_t byte, APIFrame* frame);
+
+ /**
+ * @brief Returns the current state of the parser
+ */
+ ParserState getParserState() { return parser_state; }
+private:
+ ParserState parser_state = ParserState::FIND_START;
+ uint16_t current_frame_data_index = 0;
+};
+
+} // namespace Xbee
\ No newline at end of file
diff --git a/src/shared/drivers/Xbee/APIFrames.h b/src/shared/drivers/Xbee/APIFrames.h
new file mode 100644
index 000000000..401b76b9a
--- /dev/null
+++ b/src/shared/drivers/Xbee/APIFrames.h
@@ -0,0 +1,475 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <cstring>
+#include <ostream>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#ifndef COMPILE_FOR_X86
+#include <interfaces/endianness.h>
+#elif defined(__GNUC__)
+
+#define swapBytes64 __builtin_bswap64
+
+static uint16_t swapBytes16(uint16_t x)
+{
+ return (x & 0xFF00) >> 8 | (x & 0x00FF) << 8;
+}
+#endif
+
+using std::min;
+using std::string;
+
+namespace Xbee
+{
+/// Maximum length for a TX/RX frame payload. User-configurable using the "NP"
+/// AT command or the Xbee configuration software (see Xbee datasheet)
+static constexpr size_t MIN_RX_PACKET_FRAME_SIZE = 11;
+static constexpr size_t MIN_TX_REQUEST_FRAME_SIZE = 13;
+static constexpr size_t MIN_AT_COMMAND_FRAME_SIZE = 3;
+static constexpr size_t MIN_AT_RESPONSE_FRAME_SIZE = 4;
+
+static constexpr size_t TX_STATUS_FRAME_SIZE = 6;
+static constexpr size_t MODEM_STATUS_FRAME_SIZE = 1;
+
+static constexpr uint16_t MAX_PACKET_PAYLOAD_LENGTH = 256;
+static constexpr uint16_t MAX_AT_COMMAND_PARAMS_LENGTH = 20;
+static constexpr uint16_t MAX_AT_COMMAND_RESPONSE_LENGTH = 30;
+
+static constexpr size_t FRAME_DATA_SIZE =
+ MAX_PACKET_PAYLOAD_LENGTH + MIN_TX_REQUEST_FRAME_SIZE;
+
+static constexpr size_t MAX_API_FRAME_SIZE = FRAME_DATA_SIZE + 5;
+static constexpr size_t MIN_API_FRAME_SIZE = 5;
+
+static constexpr uint64_t ADDRESS_BROADCAST = 0xFFFF;
+static constexpr uint8_t START_DELIMITER = 0x7E;
+
+enum FrameType : uint8_t
+{
+ FTYPE_AT_COMMAND = 0x08,
+ FTYPE_AT_COMMAND_QUEUE = 0x09,
+ FTYPE_TX_REQUEST = 0x10,
+ FTYPE_AT_COMMAND_RESPONSE = 0x88,
+ FTYPE_MODEM_STATUS = 0x8A,
+ FTYPE_TX_STATUS = 0x8B,
+ FTYPE_RX_PACKET_FRAME = 0x90,
+};
+
+enum TransmitOptionsBitfield : uint8_t
+{
+ TO_DISABLE_ACK = 0x01,
+ TO_DISABLE_RD = 0x02,
+ TO_NACK = 0x04,
+ TO_TRACE_ROUTE = 0x08,
+
+ TO_DM_POINT_MULTIPOINT = 0x40,
+ TO_DM_REPEATER_MODE = 0x80,
+ TO_DM_DIGIMESH = 0xC0
+};
+
+enum CommandStatusBitfield : uint8_t
+{
+ CS_OK = 0x00,
+ CS_ERROR = 0x01,
+ CS_INVALID_COMMAND = 0x02,
+ CS_INVALID_PARAMETER = 0x03,
+ CS_RSSI_INVALID = 0x40,
+ CS_IS_REMOTE_COMMAND = 0x80
+};
+
+enum ModemStatus : uint8_t
+{
+ MS_HARDWARE_RESET = 0x00,
+ MS_WATCHDOG_TIMER_RESET = 0x01,
+ MS_NETWORK_WOKE_UP = 0x0B,
+ MS_NETWORK_WENT_SLEEP = 0x0C,
+};
+
+enum DeliveryStatus : uint8_t
+{
+ DELS_SUCCESS = 0x00,
+ DELS_MAC_ACK_FAILURE = 0x01,
+ DELS_COLL_AVOID_FAILURE = 0x02,
+ DELS_NO_SPECTRUM_AVAILABLE = 0x03,
+ DELS_NET_ACK_FAILURE = 0x21,
+ DELS_ROUTE_NOT_FOUND = 0x25,
+ DELS_INT_RESOURCE_ERR = 0x31,
+ DELS_INTERNAL_ERROR = 0x32,
+ DELS_PAYLOAD_TOO_LARGE = 0x74,
+ DELS_INDIRECT_MSG_REQ = 0x75,
+};
+
+enum DiscoveryStatus : uint8_t
+{
+ DISCS_NO_DISC_OVERHEAD = 0x00,
+ DISCS_ROUTE_DISCOVERY = 0x02
+};
+
+enum ReceiveOptions : uint8_t
+{
+ RO_PACKET_ACK = 0x00,
+ RO_PACKET_IS_BROADCAST = 0x01,
+ RO_POINT_MULTIPOINT = 0x40,
+ RO_REPEATER_MODE = 0x80,
+ RO_DIGIMESH = 0xC0,
+};
+
+#pragma pack(1)
+struct APIFrame
+{
+ uint8_t start_del = 0x7E;
+ uint16_t length = 0;
+ uint8_t frame_type = 0;
+ uint8_t frame_data[FRAME_DATA_SIZE];
+
+ uint8_t checksum = 0;
+ // Used for logging, not part of the standard Xbee API Frame
+ long long timestamp = 0;
+
+ APIFrame() { memset(frame_data, 0, FRAME_DATA_SIZE); }
+
+ uint16_t getFrameDataLength() const
+ {
+ size_t len = swapBytes16(length) - 1;
+ assert(len <= FRAME_DATA_SIZE);
+
+ return min(len, FRAME_DATA_SIZE);
+ }
+
+ void setFrameDataLength(uint16_t len)
+ {
+ assert(len <= FRAME_DATA_SIZE);
+ length = swapBytes16(min((size_t)(len + 1), FRAME_DATA_SIZE + 1));
+ }
+
+ bool verifyChecksum() const
+ {
+ assert(getFrameDataLength() <= FRAME_DATA_SIZE);
+ // Sum all the bytes including checksum and frame type.
+ // The sum can be stored in a uint8_t since we only care about the least
+ // significant byte.
+ uint8_t sum = checksum + frame_type;
+ for (uint16_t i = 0; i < getFrameDataLength(); ++i)
+ {
+ sum += frame_data[i];
+ }
+ return sum == 0xFF;
+ }
+
+ void calcChecksum()
+ {
+ assert(getFrameDataLength() <= FRAME_DATA_SIZE);
+ checksum = frame_type;
+ for (uint16_t i = 0; i < getFrameDataLength(); ++i)
+ {
+ checksum += frame_data[i];
+ }
+
+ checksum = 0xFF - checksum;
+ }
+
+ template <typename FrameType>
+ FrameType* toFrameType()
+ {
+ static_assert(
+ std::is_base_of<APIFrame, FrameType>::value ||
+ std::is_same<APIFrame, FrameType>::value,
+ "FrameType must be derived from APIFrame or be an APIFrame");
+
+ return reinterpret_cast<FrameType*>(this);
+ }
+
+ size_t toBytes(uint8_t* bytes)
+ {
+ memcpy(bytes, this, 4 + getFrameDataLength());
+
+ bytes[4 + getFrameDataLength()] = checksum;
+ return 5 + getFrameDataLength();
+ }
+
+ static bool fromBytes(uint8_t* bytes, size_t size, APIFrame* f)
+ {
+ if (size >= MIN_API_FRAME_SIZE && size <= MAX_API_FRAME_SIZE)
+ {
+ memcpy(f, bytes, size - 1);
+ f->checksum = bytes[size - 1];
+
+ return true;
+ }
+ return false;
+ }
+};
+#pragma pack()
+struct ATCommandFrame : public APIFrame
+{
+ ATCommandFrame() : APIFrame()
+ {
+ frame_type = FTYPE_AT_COMMAND;
+ setFrameDataLength(MIN_AT_COMMAND_FRAME_SIZE);
+ }
+
+ uint8_t getFrameID() const { return frame_data[0]; }
+
+ void setFrameID(uint8_t frame_id) { frame_data[0] = frame_id; }
+
+ const char* getATCommand() const
+ {
+ return reinterpret_cast<const char*>(&frame_data[1]);
+ }
+
+ void setATCommand(const char* at)
+ {
+ frame_data[1] = at[0];
+ frame_data[2] = at[1];
+ }
+
+ uint8_t* getCommandDataPointer() { return &frame_data[3]; }
+
+ uint16_t getCommandDataLength() const
+ {
+ return getFrameDataLength() - MIN_AT_COMMAND_FRAME_SIZE;
+ }
+
+ void setParameterSize(uint16_t size)
+ {
+ assert(size <= FRAME_DATA_SIZE - MIN_AT_COMMAND_FRAME_SIZE);
+ size = min((size_t)size, FRAME_DATA_SIZE - MIN_AT_COMMAND_FRAME_SIZE);
+
+ setFrameDataLength(MIN_AT_COMMAND_FRAME_SIZE + size);
+ }
+};
+static_assert(sizeof(ATCommandFrame) == sizeof(APIFrame),
+ "Size of derived classes must be the same as APIFrame class (no "
+ "additional members & no virtual functions)");
+
+struct ATCommandResponseFrame : public APIFrame
+{
+ ATCommandResponseFrame() : APIFrame()
+ {
+ frame_type = FTYPE_AT_COMMAND_RESPONSE;
+ setFrameDataLength(MIN_AT_RESPONSE_FRAME_SIZE);
+ }
+
+ uint8_t getFrameID() const { return frame_data[0]; }
+
+ void setFrameID(uint8_t frame_id) { frame_data[0] = frame_id; }
+
+ const char* getATCommand() const
+ {
+ return reinterpret_cast<const char*>(&frame_data[1]);
+ }
+
+ void setATCommand(const char* at)
+ {
+ frame_data[1] = at[0];
+ frame_data[2] = at[1];
+ }
+
+ uint8_t getCommandStatus() const { return frame_data[3]; }
+
+ void setCommandStatus(uint8_t cs) { frame_data[3] = cs; }
+
+ uint8_t* getCommandDataPointer() { return &frame_data[4]; }
+
+ uint16_t getCommandDataLength() const
+ {
+ return getFrameDataLength() - MIN_AT_RESPONSE_FRAME_SIZE;
+ }
+
+ void setCommandDataSize(uint16_t size)
+ {
+ assert(size <= FRAME_DATA_SIZE - MIN_AT_RESPONSE_FRAME_SIZE);
+ size = min((size_t)size, FRAME_DATA_SIZE - MIN_AT_RESPONSE_FRAME_SIZE);
+
+ setFrameDataLength(MIN_AT_RESPONSE_FRAME_SIZE + size);
+ }
+};
+static_assert(sizeof(ATCommandFrame) == sizeof(APIFrame),
+ "Size of derived classes must be the same as APIFrame class (no "
+ "additional members & no virtual functions)");
+
+struct TXRequestFrame : public APIFrame
+{
+ TXRequestFrame() : APIFrame()
+ {
+ frame_type = FTYPE_TX_REQUEST;
+ setFrameDataLength(MIN_TX_REQUEST_FRAME_SIZE);
+
+ // Reserved bytes
+ frame_data[9] = 0xFF;
+ frame_data[10] = 0xFE;
+ }
+
+ uint8_t getFrameID() const { return frame_data[0]; }
+
+ void setFrameID(uint8_t frame_id) { frame_data[0] = frame_id; }
+
+ uint64_t getDestAddress() const
+ {
+ uint64_t addr;
+ memcpy(&addr, &frame_data[1], sizeof(uint64_t));
+ return swapBytes64(addr);
+ }
+
+ void setDestAddress(uint64_t address)
+ {
+ address = swapBytes64(address);
+ memcpy(&frame_data[1], &address, 8);
+ }
+
+ uint8_t getBroadcastRadius() const { return frame_data[11]; }
+
+ void setBroadcastRadius(uint8_t br) { frame_data[11] = br; }
+
+ uint8_t getTrasmitOptions() const { return frame_data[12]; }
+
+ void setTransmitOptions(uint8_t br) { frame_data[12] = br; }
+
+ uint8_t* getRFDataPointer() { return &frame_data[13]; }
+
+ uint16_t getRFDataLength() const
+ {
+ return min((size_t)(getFrameDataLength() - MIN_TX_REQUEST_FRAME_SIZE),
+ (size_t)MAX_PACKET_PAYLOAD_LENGTH);
+ }
+
+ void setRFDataLength(uint16_t size)
+ {
+ assert(size <= MAX_PACKET_PAYLOAD_LENGTH);
+ size = min(size, MAX_PACKET_PAYLOAD_LENGTH);
+
+ setFrameDataLength(MIN_TX_REQUEST_FRAME_SIZE + size);
+ }
+};
+static_assert(sizeof(TXRequestFrame) == sizeof(APIFrame),
+ "Size of derived classes must be the same as APIFrame class (no "
+ "additional members & no virtual functions)");
+
+struct ModemStatusFrame : public APIFrame
+{
+ ModemStatusFrame() : APIFrame()
+ {
+ frame_type = FTYPE_MODEM_STATUS;
+ setFrameDataLength(MODEM_STATUS_FRAME_SIZE);
+ }
+
+ uint8_t getStatus() const { return frame_data[0]; }
+
+ void setStatus(uint8_t status) { frame_data[0] = status; }
+};
+static_assert(sizeof(ModemStatusFrame) == sizeof(APIFrame),
+ "Size of derived classes must be the same as APIFrame class (no "
+ "additional members & no virtual functions)");
+
+struct TXStatusFrame : public APIFrame
+{
+ TXStatusFrame() : APIFrame()
+ {
+ frame_type = FTYPE_TX_STATUS;
+ setFrameDataLength(TX_STATUS_FRAME_SIZE);
+
+ // Reserved bytes
+ frame_data[1] = 0xFF;
+ frame_data[2] = 0xFE;
+ }
+
+ uint8_t getFrameID() const { return frame_data[0]; }
+
+ void setFrameID(uint8_t frame_id) { frame_data[0] = frame_id; }
+
+ uint8_t getTransmitRetryCount() const { return frame_data[3]; }
+
+ void setTransmitRetryCount(uint8_t trc) { frame_data[3] = trc; }
+
+ uint8_t getDeliveryStatus() const { return frame_data[4]; }
+
+ void setDeliveryStatus(uint8_t ds) { frame_data[4] = ds; }
+
+ uint8_t getDiscoveryStatus() const { return frame_data[5]; }
+
+ void setDiscoveryStatus(uint8_t ds) { frame_data[5] = ds; }
+};
+
+static_assert(sizeof(TXStatusFrame) == sizeof(APIFrame),
+ "Size of derived classes must be the same as APIFrame class (no "
+ "additional members & no virtual functions)");
+
+struct RXPacketFrame : public APIFrame
+{
+ RXPacketFrame() : APIFrame()
+ {
+ frame_type = FTYPE_RX_PACKET_FRAME;
+ setFrameDataLength(MIN_RX_PACKET_FRAME_SIZE);
+
+ frame_data[8] = 0xFF;
+ frame_data[9] = 0xFE;
+ }
+
+ uint64_t getSourceAddress() const
+ {
+ uint64_t addr;
+ memcpy(&addr, &frame_data[0], sizeof(uint64_t));
+ return swapBytes64(addr);
+ }
+
+ void setSourceAddress(uint64_t address)
+ {
+ address = swapBytes64(address);
+ uint8_t* addr = reinterpret_cast<uint8_t*>(&address);
+
+ memcpy(&frame_data[0], addr, 8);
+ }
+
+ uint8_t getReceiveOptions() const { return frame_data[10]; }
+
+ void setReceiveOptions(uint8_t ro) { frame_data[10] = ro; }
+
+ uint8_t* getRXDataPointer() { return &frame_data[11]; }
+
+ uint16_t getRXDataLength() const
+ {
+ return min((size_t)(getFrameDataLength() - MIN_RX_PACKET_FRAME_SIZE),
+ (size_t)MAX_PACKET_PAYLOAD_LENGTH);
+ }
+
+ void setRXDataLength(uint16_t size)
+ {
+ assert(size <= MAX_PACKET_PAYLOAD_LENGTH);
+ size = min(size, MAX_PACKET_PAYLOAD_LENGTH);
+
+ setFrameDataLength(MIN_RX_PACKET_FRAME_SIZE + size);
+ }
+};
+
+static_assert(sizeof(RXPacketFrame) == sizeof(APIFrame),
+ "Size of derived classes must be the same as APIFrame class (no "
+ "additional members & no virtual functions)");
+} // namespace Xbee
\ No newline at end of file
diff --git a/src/shared/drivers/Xbee/APIFramesLog.h b/src/shared/drivers/Xbee/APIFramesLog.h
new file mode 100644
index 000000000..d97736f8a
--- /dev/null
+++ b/src/shared/drivers/Xbee/APIFramesLog.h
@@ -0,0 +1,394 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <cstdint>
+#include <ostream>
+#include <string>
+#include <utility>
+
+#include "APIFrames.h"
+
+using std::min;
+
+/**
+ * Classes used if you want to log an APIFrame, but do not want to log the base
+ * APIFrame class, which is quite heavy on memory
+ */
+
+namespace Xbee
+{
+
+struct APIFrameLog
+{
+ long long timestamp;
+ uint8_t frame_type;
+ uint16_t frame_data_length;
+ uint8_t frame_data[FRAME_DATA_SIZE];
+
+ static bool fromAPIFrame(APIFrame& api, APIFrameLog* dest)
+ {
+ dest->timestamp = api.timestamp;
+ dest->frame_type = api.frame_type;
+ dest->frame_data_length = api.getFrameDataLength();
+
+ memcpy(dest->frame_data, api.frame_data, dest->frame_data_length);
+
+ return true;
+ }
+
+ static string header() { return "timestamp,length,frame_type\n"; }
+
+ void print(std::ostream& os) const
+ {
+ os << timestamp << "," << frame_data_length << "," << frame_type
+ << "\n";
+ }
+};
+
+struct ATCommandFrameLog
+{
+ long long timestamp;
+ uint8_t frame_id = 0;
+ char at_command[2];
+
+ uint8_t command_data[MAX_AT_COMMAND_PARAMS_LENGTH];
+ uint16_t command_data_length = 0;
+
+ static bool toFrameType(APIFrame& api, ATCommandFrameLog* dest)
+ {
+ if (api.frame_type != FTYPE_AT_COMMAND)
+ {
+ return false;
+ }
+
+ if (api.getFrameDataLength() < MIN_AT_COMMAND_FRAME_SIZE ||
+ api.getFrameDataLength() >
+ MIN_AT_COMMAND_FRAME_SIZE + MAX_AT_COMMAND_PARAMS_LENGTH)
+ {
+ return false;
+ }
+ ATCommandFrame* at = api.toFrameType<ATCommandFrame>();
+
+ dest->timestamp = at->timestamp;
+ dest->frame_id = at->getFrameID();
+
+ memcpy(dest->at_command, at->getATCommand(), 2);
+
+ dest->command_data_length = at->getCommandDataLength();
+ memcpy(dest->command_data, at->getCommandDataPointer(),
+ dest->command_data_length);
+
+ return true;
+ }
+
+ static string header() { return "timestamp,id,cmd,param_size,params\n"; }
+
+ void print(std::ostream& os) const
+ {
+ char cmd[3];
+
+ strncpy(cmd, at_command, 2);
+ cmd[2] = '\0';
+
+ os << timestamp << "," << (int)frame_id << "," << cmd << ","
+ << command_data_length << ",";
+
+ if (command_data_length > 0)
+ {
+ for (uint16_t i = 0; i < command_data_length; i++)
+ {
+ os << (int)command_data[i] << " ";
+ }
+ }
+ else
+ {
+ os << "-";
+ }
+
+ os << "\n";
+ }
+};
+
+struct TXRequestFrameLog
+{
+ long long timestamp = 0;
+ uint8_t frame_id = 0;
+
+ uint64_t dest_address = 0;
+
+ uint8_t broadcast_radius = 0;
+ uint8_t transmit_options = 0;
+
+ uint8_t rf_data[MAX_PACKET_PAYLOAD_LENGTH];
+ uint16_t rf_data_length = 0;
+
+ static bool toFrameType(APIFrame& api, TXRequestFrameLog* dest)
+ {
+ if (api.frame_type != FTYPE_TX_REQUEST)
+ {
+ return false;
+ }
+
+ if (api.getFrameDataLength() < MIN_TX_REQUEST_FRAME_SIZE ||
+ api.getFrameDataLength() >
+ MIN_TX_REQUEST_FRAME_SIZE + MAX_PACKET_PAYLOAD_LENGTH)
+ {
+ return false;
+ }
+
+ TXRequestFrame* tx = api.toFrameType<TXRequestFrame>();
+
+ dest->timestamp = api.timestamp;
+ dest->frame_id = tx->getFrameID();
+
+ dest->dest_address = tx->getDestAddress();
+
+ dest->broadcast_radius = tx->getBroadcastRadius();
+ dest->transmit_options = tx->getTrasmitOptions();
+
+ dest->rf_data_length = tx->getRFDataLength();
+ memcpy(dest->rf_data, tx->getRFDataPointer(), dest->rf_data_length);
+
+ return true;
+ }
+
+ static string header()
+ {
+ return "timestamp,id,dest_addr,broadcast_radius,tx_options,rf_data_"
+ "len,first_uint32\n";
+ }
+
+ void print(std::ostream& os) const
+ {
+ uint32_t d;
+ memcpy(&d, rf_data, sizeof(d));
+ os << timestamp << "," << (int)frame_id << "," << dest_address << ","
+ << (int)broadcast_radius << "," << (int)transmit_options << ","
+ << rf_data_length << "," << d << "\n";
+ }
+};
+
+struct ATCommandResponseFrameLog
+{
+ long long timestamp = 0;
+ uint8_t frame_id = 0;
+ char at_command[2];
+ uint8_t command_status = 0;
+
+ uint8_t command_data[MAX_AT_COMMAND_RESPONSE_LENGTH];
+ uint16_t command_data_length = 0;
+
+ static bool toFrameType(APIFrame& api, ATCommandResponseFrameLog* dest)
+ {
+ if (api.frame_type != FTYPE_AT_COMMAND_RESPONSE)
+ {
+ return false;
+ }
+
+ if (api.getFrameDataLength() < MIN_AT_COMMAND_FRAME_SIZE ||
+ api.getFrameDataLength() >
+ MIN_AT_COMMAND_FRAME_SIZE + MAX_AT_COMMAND_RESPONSE_LENGTH)
+ {
+ return false;
+ }
+
+ ATCommandResponseFrame* at = api.toFrameType<ATCommandResponseFrame>();
+
+ dest->timestamp = api.timestamp;
+ dest->frame_id = at->getFrameID();
+ memcpy(dest->at_command, at->getATCommand(), 2);
+
+ dest->command_status = at->getCommandStatus();
+
+ dest->command_data_length = at->getCommandDataLength();
+ memcpy(dest->command_data, at->getCommandDataPointer(),
+ dest->command_data_length);
+
+ return true;
+ }
+
+ static string header()
+ {
+ return "timestamp,id,cmd,status,param_size,params\n";
+ }
+
+ void print(std::ostream& os) const
+ {
+ char cmd[3];
+
+ strncpy(cmd, at_command, 2);
+ cmd[2] = '\0';
+
+ os << timestamp << "," << (int)frame_id << "," << cmd << ","
+ << (int)command_status << "," << command_data_length << ",";
+
+ if (command_data_length > 0)
+ {
+ for (uint16_t i = 0; i < command_data_length; i++)
+ {
+ os << (int)command_data[i] << " ";
+ }
+ }
+ else
+ {
+ os << "-";
+ }
+
+ os << "\n";
+ }
+};
+
+struct ModemStatusFrameLog
+{
+ long long timestamp = 0;
+ uint8_t modem_status = 0;
+
+ static bool toFrameType(APIFrame& api, ModemStatusFrameLog* dest)
+ {
+ if (api.frame_type != FTYPE_MODEM_STATUS)
+ {
+ return false;
+ }
+
+ if (api.getFrameDataLength() != MODEM_STATUS_FRAME_SIZE)
+ {
+ return false;
+ }
+
+ ModemStatusFrame* modem = api.toFrameType<ModemStatusFrame>();
+
+ dest->timestamp = api.timestamp;
+ dest->modem_status = modem->getStatus();
+
+ return true;
+ }
+
+ static string header() { return "timestamp,status\n"; }
+
+ void print(std::ostream& os) const
+ {
+ os << timestamp << "," << (int)modem_status << "\n";
+ }
+};
+
+struct TXStatusFrameLog
+{
+ long long timestamp = 0;
+ uint8_t frame_id = 0;
+ uint8_t tx_retry_count = 0;
+ uint8_t delivery_status = 0;
+ uint8_t discovery_status = 0;
+
+ static bool toFrameType(APIFrame& api, TXStatusFrameLog* dest)
+ {
+ if (api.frame_type != FTYPE_TX_STATUS)
+ {
+ return false;
+ }
+
+ if (api.getFrameDataLength() != TX_STATUS_FRAME_SIZE)
+ {
+ return false;
+ }
+
+ TXStatusFrame* tx = api.toFrameType<TXStatusFrame>();
+
+ dest->timestamp = api.timestamp;
+ dest->frame_id = tx->getFrameID();
+
+ dest->tx_retry_count = tx->getTransmitRetryCount();
+ dest->delivery_status = tx->getDeliveryStatus();
+ dest->discovery_status = tx->getDiscoveryStatus();
+
+ return true;
+ }
+
+ static string header()
+ {
+ return "timestamp,id,tx_retries,delivery_status,discovery_status\n";
+ }
+
+ void print(std::ostream& os) const
+ {
+ os << timestamp << "," << (int)frame_id << "," << (int)tx_retry_count
+ << "," << (int)delivery_status << "," << (int)discovery_status
+ << "\n";
+ }
+};
+
+struct RXPacketFrameLog
+{
+ long long timestamp = 0;
+
+ uint64_t source_address = 0;
+
+ uint8_t receive_options = 0;
+
+ uint8_t rx_data[MAX_PACKET_PAYLOAD_LENGTH];
+ uint16_t rx_data_length = 0;
+
+ static bool toFrameType(APIFrame& api, RXPacketFrameLog* dest)
+ {
+ if (api.frame_type != FTYPE_RX_PACKET_FRAME)
+ {
+ return false;
+ }
+
+ if (api.getFrameDataLength() < MIN_RX_PACKET_FRAME_SIZE ||
+ api.getFrameDataLength() >
+ MIN_RX_PACKET_FRAME_SIZE + MAX_PACKET_PAYLOAD_LENGTH)
+ {
+ return false;
+ }
+
+ RXPacketFrame* rx = api.toFrameType<RXPacketFrame>();
+
+ dest->timestamp = api.timestamp;
+ dest->source_address = rx->getSourceAddress();
+
+ dest->receive_options = rx->getReceiveOptions();
+
+ dest->rx_data_length = rx->getRXDataLength();
+ memcpy(dest->rx_data, rx->getRXDataPointer(), dest->rx_data_length);
+
+ return true;
+ }
+
+ static string header()
+ {
+ return "timestamp,src_addr,rx_options,rx_data_size,payload32_0,payload32_1\n";
+ }
+
+ void print(std::ostream& os) const
+ {
+ uint32_t data[2];
+ memcpy(&data, rx_data, min(sizeof(uint32_t)*2, (size_t)rx_data_length));
+ os << timestamp << "," << source_address << "," << (int)receive_options
+ << "," << rx_data_length << "," << data[0] << "," << data[1] << "\n";
+ }
+};
+
+} // namespace Xbee
\ No newline at end of file
diff --git a/src/shared/drivers/Xbee/ATCommands.h b/src/shared/drivers/Xbee/ATCommands.h
new file mode 100644
index 000000000..09280f9b7
--- /dev/null
+++ b/src/shared/drivers/Xbee/ATCommands.h
@@ -0,0 +1,155 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "APIFrames.h"
+#include "Xbee.h"
+
+namespace Xbee
+{
+
+/**
+ * @brief Enables or disable the specified channels. Channel 9 and 24 are always
+ * disabled due to regulatory limitations.
+ * See datasheet for the list of channel frequencies.
+ *
+ * @param xbee Reference to an xbee object
+ * @param channels channel mask
+ * @return True if the command was executed successfully
+ */
+bool setChannelMask(Xbee& xbee, uint32_t mask, unsigned int timeout = 1000)
+{
+ ATCommandResponseFrame response;
+
+ mask = swapBytes32(mask);
+
+ return xbee.sendATCommand("CM", &response,
+ reinterpret_cast<uint8_t*>(&mask), 4, timeout);
+}
+
+/**
+ * @brief Enables or disable the specified channels. Channel 9 and 24 are always
+ * disabled due to regulatory limitations.
+ * See datasheet for the list of channel frequencies.
+ *
+ * @param xbee Reference to an xbee object
+ * @param channels channel mask array
+ *
+ * @return True if the command was executed successfully
+ */
+bool setChannelMask(Xbee& xbee, bool channels[30], unsigned int timeout = 1000)
+{
+ uint32_t val = 0;
+
+ for (int i = 0; i < 30; i++)
+ {
+ val &= ((uint32_t)channels[i]) << i;
+ }
+
+ return setChannelMask(xbee, val, timeout);
+}
+
+/**
+ * @brief Enable communication on all available channels. Equivalent to calling
+ * setChannelMask(xbee, 0x3EFFFDFF).
+ *
+ * @param xbee Reference to an xbee object
+ * @return True if the command was executed successfully
+ */
+bool enableAllChannels(Xbee& xbee, unsigned int timeout = 1000)
+{
+ return setChannelMask(xbee, 0x3EFFFDFF, timeout);
+}
+
+/**
+ * @brief Disables frequency hopping by allowing communication only on channel
+ * #29. Equivalent to calling setChannelMask(xbee, 0x20000000).
+ *
+ * @param xbee Reference to an xbee object
+ */
+bool disableFrequencyHopping(Xbee& xbee, unsigned int timeout = 1000)
+{
+ return setChannelMask(xbee, 0x20000000, timeout);
+}
+
+/**
+ * @brief Configures the desired xbee data rate.
+ *
+ * @param xbee Reference to an xbee object
+ * @param data_rate_80kbps true for 80kbps, false for 10kbps
+ * @return True if the command was executed successfully
+ */
+bool setDataRate(Xbee& xbee, bool data_rate_80kbps, unsigned int timeout = 1000)
+{
+ uint8_t param = (uint8_t)data_rate_80kbps;
+ ATCommandResponseFrame response;
+
+ return xbee.sendATCommand("BR", &response, ¶m, 1, timeout);
+}
+
+/**
+ * @brief Writes parameter values to non-volatile memory so that parameter
+ * modifications persist through subsequent resets.
+ *
+ * @param xbee Reference to an xbee object
+ * @return True if the command was executed successfully
+ */
+bool writeToMemory(Xbee& xbee, unsigned int timeout = 1000)
+{
+ ATCommandResponseFrame response;
+ return xbee.sendATCommand("WR", &response, nullptr, 0, timeout);
+}
+
+/**
+ * @brief Performs an energy detect scan on all channels
+ *
+ * @param xbee Reference to an xbee object
+ * @param energy_detect_data Pointer to a 30-bytes buffer where energy levels
+ * will be stored. Energy levels are in -dBm units.
+ * @param duration Scan duration in ms
+ * @return True if the command was executed successfully and data stored in
+ * energy_detect_data is valid
+ */
+bool energyDetect(Xbee& xbee, int* energy_detect_data, uint8_t duration,
+ unsigned int timeout = 1000)
+{
+ ATCommandResponseFrame response;
+ if (xbee.sendATCommand("ED", &response, &duration, 1, timeout))
+ {
+ uint16_t resp_len = response.getCommandDataLength();
+ if (resp_len == 30)
+ {
+ for (int i = 0; i < 30; i++)
+ {
+ energy_detect_data[i] =
+ (int)(*(response.getCommandDataPointer() + i));
+ }
+
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace Xbee
diff --git a/src/shared/drivers/Xbee/Xbee.cpp b/src/shared/drivers/Xbee/Xbee.cpp
index 4cc53fb6b..965f158ed 100644
--- a/src/shared/drivers/Xbee/Xbee.cpp
+++ b/src/shared/drivers/Xbee/Xbee.cpp
@@ -1,17 +1,17 @@
/**
- * Copyright (c) 2019 Skyward Experimental Rocketry
- * Authors: Luca Erbetta
- *
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
* 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
@@ -23,7 +23,512 @@
#include "Xbee.h"
+#include <Debug.h>
+#include <kernel/scheduler/scheduler.h>
+#include <miosix.h>
+
+#include <algorithm>
+
+using miosix::FastMutex;
+using miosix::Lock;
+using miosix::Unlock;
+using std::min;
+
namespace Xbee
{
-miosix::Thread* waiting = nullptr;
-}
\ No newline at end of file
+
+Xbee::Xbee(SPIBusInterface& bus, GpioType cs, GpioType attn, GpioType rst,
+ long long tx_timeout)
+ : Xbee(bus, {}, cs, attn, rst, tx_timeout)
+{
+ spi_xbee.config.clock_div = SPIClockDivider::DIV128;
+}
+
+Xbee::Xbee(SPIBusInterface& bus, SPIBusConfig config, GpioType cs,
+ GpioType attn, GpioType rst, long long tx_timeout)
+ : spi_xbee(bus, cs, config), attn(attn), rst(rst), tx_timeout(tx_timeout)
+{
+ reset();
+}
+
+Xbee::~Xbee() { wakeReceiver(true); }
+
+bool Xbee::send(uint8_t* pkt, size_t pkt_len)
+{
+ if (pkt_len > MAX_PACKET_PAYLOAD_LENGTH || pkt_len == 0)
+ {
+ TRACE("[Xbee] Invalid packet length (0< %u <= %u)\n", pkt_len,
+ MAX_PACKET_PAYLOAD_LENGTH);
+ return false;
+ }
+ long long start_tick = miosix::getTick();
+
+ TXRequestFrame tx_req;
+ uint8_t tx_frame_id = buildTXRequestFrame(tx_req, pkt, pkt_len);
+ bool success = false;
+ bool status_timeout = true;
+ {
+ Lock<FastMutex> l(mutex_xbee_comm);
+ writeFrame(tx_req);
+
+ // Wait for a TX Status frame
+ long long timeout_tick = miosix::getTick() + tx_timeout;
+
+ while (waitForFrame(FTYPE_TX_STATUS, FRAME_POLL_INTERVAL, timeout_tick))
+ {
+ TXStatusFrame* f = parsing_api_frame.toFrameType<TXStatusFrame>();
+
+ if (f->getFrameID() == tx_frame_id)
+ {
+ success = f->getDeliveryStatus() == DELS_SUCCESS;
+ status_timeout = false;
+ break;
+ }
+ else
+ {
+ TRACE("[Xbee] Wrong tx_status ID\n");
+ }
+ }
+
+ // Ak for total transmitted byte count for logging purposes
+ sendATCommandInternal("BC");
+ }
+
+ // If there is more data to receive, wake the receive() thread
+ if (attn.value() == 0)
+ {
+ wakeReceiver();
+ }
+ if (status_timeout)
+ {
+ ++status.tx_timeout_count;
+ TRACE("[Xbee] TX_STATUS timeout\n");
+ }
+ time_to_send_stats.add(miosix::getTick() - start_tick);
+ return success;
+}
+
+ssize_t Xbee::receive(uint8_t* buf, size_t buf_max_size)
+{
+ while (true)
+ {
+ // We have data in the buffer pending to be returned
+ if (!rx_frames_buf.isEmpty() || curr_rx_payload_ptr >= 0)
+ {
+ return fillReceiveBuf(buf, buf_max_size);
+ }
+ // No data in the buffer, but the xbee has data to return via SPI
+ else if (attn.value() == 0)
+ {
+ Lock<FastMutex> l(mutex_xbee_comm);
+ if (readRXFrame())
+ {
+ sendATCommandInternal("DB"); // Query last packet RSSI
+ sendATCommandInternal("ER"); // Query receive error count
+ }
+ }
+ // No data available at all: sleep until we have something to do
+ else
+ {
+ {
+ miosix::FastInterruptDisableLock dLock;
+ receive_thread = miosix::Thread::getCurrentThread();
+
+ while (receive_thread != 0) // Avoid spurious wakeups
+ {
+ receive_thread->IRQwait();
+ {
+ miosix::FastInterruptEnableLock eLock(dLock);
+ miosix::Thread::yield();
+ }
+ }
+ }
+
+ // Forcefully return without receiving anything
+ if (force_rcv_return)
+ {
+ force_rcv_return = false;
+ return -1;
+ }
+ }
+ }
+}
+
+XbeeStatus Xbee::getStatus()
+{
+ status.timestamp = miosix::getTick();
+ status.time_to_send_stats = time_to_send_stats.getStats();
+ return status;
+}
+
+void Xbee::reset()
+{
+ Lock<FastMutex> l(mutex_xbee_comm);
+ {
+ miosix::FastInterruptDisableLock dLock();
+ rst.mode(miosix::Mode::OPEN_DRAIN);
+ }
+ rst.low();
+ miosix::delayUs(50);
+ rst.high();
+
+ // When the xbee is ready, we should assert SSEL to tell it to use
+ // SPI, and it should provide us with a modem status frame
+ long long timeout = miosix::getTick() + 1000;
+ do
+ {
+ // Assert SSEL on every iteration as we don't exactly know when the
+ // xbee will be ready.
+ {
+ SPIAcquireLock acq(spi_xbee);
+ spi_xbee.cs.low();
+ miosix::delayUs(10);
+ spi_xbee.cs.high();
+ }
+
+ miosix::delayUs(50);
+
+ if (attn.value() == 0 && readOneFrame() == ParseResult::SUCCESS)
+ {
+ handleFrame(parsing_api_frame);
+
+ if (parsing_api_frame.frame_type == FTYPE_MODEM_STATUS)
+ {
+ break;
+ }
+ }
+ miosix::Thread::sleep(5);
+ } while (miosix::getTick() < timeout);
+}
+
+void Xbee::wakeReceiver(bool force_return)
+{
+ force_rcv_return = force_return;
+ miosix::FastInterruptDisableLock dLock;
+
+ if (receive_thread)
+ {
+ receive_thread->IRQwakeup();
+ receive_thread = 0;
+ }
+}
+
+void Xbee::handleATTNInterrupt()
+{
+ if (receive_thread)
+ {
+ receive_thread->IRQwakeup();
+ if (receive_thread->IRQgetPriority() >
+ miosix::Thread::IRQgetCurrentThread()->IRQgetPriority())
+ {
+ miosix::Scheduler::IRQfindNextThread();
+ }
+ receive_thread = 0;
+ }
+}
+
+size_t Xbee::fillReceiveBuf(uint8_t* buf, size_t buf_max_size)
+{
+ if (!rx_frames_buf.isEmpty() && curr_rx_payload_ptr == -1)
+ {
+ Lock<FastMutex> l(mutex_rx_frames);
+ curr_rx_frame = rx_frames_buf.pop();
+ curr_rx_payload_ptr = 0;
+ }
+
+ if (curr_rx_payload_ptr >= 0)
+ {
+ size_t len_remaining_data =
+ curr_rx_frame.getRXDataLength() - curr_rx_payload_ptr;
+
+ // The buffer may be smaller than the data we need to return
+ size_t len_to_copy = min(buf_max_size, len_remaining_data);
+
+ memcpy(buf, curr_rx_frame.getRXDataPointer() + curr_rx_payload_ptr,
+ len_to_copy);
+
+ curr_rx_payload_ptr += len_to_copy;
+
+ if (curr_rx_payload_ptr == curr_rx_frame.getRXDataLength())
+ {
+ // We've emptied the current frame
+ curr_rx_payload_ptr = -1;
+ }
+
+ return len_to_copy;
+ }
+
+
+ return 0;
+}
+
+ParseResult Xbee::readOneFrame()
+{
+ SPIAcquireLock acq(spi_xbee);
+ SPISelectLock sel(spi_xbee);
+
+ ParseResult result = ParseResult::IDLE;
+ do
+ {
+ result = parser.parse(spi_xbee.bus.read(), &parsing_api_frame);
+ } while (attn.value() == 0 && result == ParseResult::PARSING);
+
+ return result;
+}
+
+bool Xbee::readRXFrame()
+{
+ while (attn.value() == 0)
+ {
+ if (readOneFrame() == ParseResult::SUCCESS)
+ {
+ handleFrame(parsing_api_frame);
+
+ if (parsing_api_frame.frame_type == FTYPE_RX_PACKET_FRAME)
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void Xbee::writeFrame(APIFrame& frame)
+{
+ frame.timestamp = miosix::getTick(); // Only for logging purposes
+
+ // Serialize the frame
+ uint8_t tx_buf[MAX_API_FRAME_SIZE];
+ size_t tx_buf_size = frame.toBytes(tx_buf);
+
+ {
+ SPITransaction spi(spi_xbee);
+ spi.transfer(tx_buf, tx_buf_size);
+ }
+
+ // Pass the frame we just sent to the listener
+ if (frame_listener)
+ {
+ frame_listener(frame);
+ }
+
+ // Full duplex spi transfer: we may have received valid data while we
+ // were sending the frame.
+ for (unsigned int i = 0; i < tx_buf_size; i++)
+ {
+ ParseResult res = parser.parse(tx_buf[i], &parsing_api_frame);
+ if (res == ParseResult::SUCCESS)
+ {
+ handleFrame(parsing_api_frame);
+ }
+ }
+}
+
+bool Xbee::waitForFrame(uint8_t frame_type, unsigned int poll_interval,
+ long long timeout_tick)
+{
+ do
+ {
+ if (attn.value() == 0)
+ {
+ if (readOneFrame() == ParseResult::SUCCESS)
+ {
+ handleFrame(parsing_api_frame);
+
+ if (parsing_api_frame.frame_type == frame_type)
+ {
+ return true;
+ }
+ }
+ }
+ else
+ {
+ miosix::Thread::sleep(poll_interval);
+ }
+ } while (miosix::getTick() < timeout_tick);
+
+ return false;
+}
+
+uint8_t Xbee::buildTXRequestFrame(TXRequestFrame& tx_req, uint8_t* pkt,
+ size_t pkt_len)
+{
+ memcpy(tx_req.getRFDataPointer(), pkt, pkt_len);
+ tx_req.setRFDataLength(pkt_len);
+
+ uint8_t tx_frame_id = getNewFrameID();
+
+ tx_req.setFrameID(tx_frame_id);
+
+ tx_req.setDestAddress(ADDRESS_BROADCAST);
+ tx_req.setBroadcastRadius(0); // 0 = max hops, but we don't really care as
+ // it does not apply to point-multipoint mode
+ // Point-multipoint mode, disable ack, disable route discovery
+ tx_req.setTransmitOptions(TO_DM_POINT_MULTIPOINT | TO_DISABLE_ACK |
+ TO_DISABLE_RD);
+ tx_req.calcChecksum();
+
+ return tx_frame_id;
+}
+
+void Xbee::sendATCommand(const char* cmd, uint8_t* params, size_t params_len)
+{
+ Lock<FastMutex> l(mutex_xbee_comm);
+
+ sendATCommandInternal(0, cmd, params, params_len);
+}
+
+bool Xbee::sendATCommand(const char* cmd, ATCommandResponseFrame* response,
+ uint8_t* params, size_t params_len,
+ unsigned int timeout)
+{
+ Lock<FastMutex> l(mutex_xbee_comm);
+
+ uint8_t tx_frame_id = sendATCommandInternal(cmd, params, params_len);
+
+ bool success = false;
+
+ long long timeout_tick = miosix::getTick() + timeout;
+
+ while (waitForFrame(FTYPE_AT_COMMAND_RESPONSE, FRAME_POLL_INTERVAL,
+ timeout_tick))
+ {
+ ATCommandResponseFrame* f =
+ parsing_api_frame.toFrameType<ATCommandResponseFrame>();
+
+ if (f->getFrameID() == tx_frame_id &&
+ strncmp(cmd, f->getATCommand(), 2) == 0)
+ {
+ memcpy(response, f, sizeof(ATCommandResponseFrame));
+ success = true;
+ break;
+ }
+ }
+
+ return success;
+}
+
+uint8_t Xbee::sendATCommandInternal(const char* cmd, uint8_t* params,
+ size_t params_len)
+{
+ return sendATCommandInternal(getNewFrameID(), cmd, params, params_len);
+}
+
+uint8_t Xbee::sendATCommandInternal(uint8_t tx_frame_id, const char* cmd,
+ uint8_t* params, size_t params_len)
+{
+ // Build the AT command
+ ATCommandFrame at;
+ at.setATCommand(cmd);
+ at.setFrameID(tx_frame_id);
+ if (params_len > 0)
+ {
+ if (params_len > MAX_AT_COMMAND_PARAMS_LENGTH)
+ {
+ TRACE(
+ "[Xbee] ERROR! AT Command payload too large. It was "
+ "truncated.\n");
+ params_len = MAX_AT_COMMAND_PARAMS_LENGTH;
+ }
+ at.setParameterSize(params_len);
+ memcpy(at.getCommandDataPointer(), params, params_len);
+ }
+
+ at.calcChecksum();
+
+ // Send it
+ writeFrame(at);
+
+ return tx_frame_id;
+}
+
+void Xbee::handleFrame(APIFrame& frame)
+{
+ // Set the timestamp to the frame
+ frame.timestamp = miosix::getTick();
+
+ switch (frame.frame_type)
+ {
+ case FTYPE_RX_PACKET_FRAME:
+ {
+ RXPacketFrame* pf = frame.toFrameType<RXPacketFrame>();
+ {
+ Lock<FastMutex> l(mutex_rx_frames);
+
+ if (rx_frames_buf.isFull())
+ {
+ ++status.rx_dropped_buffers;
+ }
+ rx_frames_buf.put(*pf);
+ if (rx_frames_buf.count() > status.frame_buf_max_length)
+ {
+ status.frame_buf_max_length = rx_frames_buf.count();
+ }
+ }
+ wakeReceiver();
+ break;
+ }
+ case FTYPE_MODEM_STATUS:
+ {
+ ModemStatusFrame* ms = frame.toFrameType<ModemStatusFrame>();
+
+ switch (ms->getStatus())
+ {
+ case MS_HARDWARE_RESET:
+ TRACE("[Xbee] Modem status: Hardware reset\n");
+ break;
+ case MS_WATCHDOG_TIMER_RESET:
+ TRACE("[Xbee] Modem status: Watchdog timer reset\n");
+ break;
+ }
+ break;
+ }
+ case FTYPE_TX_STATUS:
+ {
+ TXStatusFrame* ts = frame.toFrameType<TXStatusFrame>();
+ status.last_tx_status = ts->getDeliveryStatus();
+ if (status.last_tx_status != DELS_SUCCESS)
+ {
+ status.last_tx_status_error = status.last_tx_status;
+ }
+ switch (status.last_tx_status)
+ {
+ case DELS_SUCCESS:
+ break;
+ default:
+ TRACE(
+ "TX Status Error: %d (retries: %d, RD: %s)\n",
+ status.last_tx_status, ts->getTransmitRetryCount(),
+ ts->getDiscoveryStatus() == 2 ? "Enabled" : "Disabled");
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Pass the frame to the listener
+ if (frame_listener)
+ {
+ frame_listener(frame);
+ }
+}
+
+void Xbee::setOnFrameReceivedListener(OnFrameReceivedListener listener)
+{
+ frame_listener = listener;
+}
+
+uint8_t Xbee::getNewFrameID()
+{
+ uint8_t tx_frame_id = frame_id_counter++;
+
+ // Any value != 0 implies that we DO want a tx status response
+ if (frame_id_counter == 0)
+ frame_id_counter = 1;
+
+ return tx_frame_id;
+}
+
+} // namespace Xbee
\ No newline at end of file
diff --git a/src/shared/drivers/Xbee/Xbee.h b/src/shared/drivers/Xbee/Xbee.h
index cea5d667c..8b5cfb0e2 100644
--- a/src/shared/drivers/Xbee/Xbee.h
+++ b/src/shared/drivers/Xbee/Xbee.h
@@ -1,5 +1,6 @@
-/* Copyright (c) 2015-2019 Skyward Experimental Rocketry
- * Authors: Andrea Milluzzo, Artem Glukhov, Alvise de Faveri Tron, Luca Erbetta
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,628 +23,259 @@
#pragma once
-#include <Common.h>
-#include <drivers/Transceiver.h>
-#include <drivers/spi/SPIDriver.h>
#include <miosix.h>
-#include <algorithm>
-#include <vector>
+#include <functional>
-#include "ActiveObject.h"
#include "XbeeStatus.h"
-#include "diagnostic/StackLogger.h"
-
-using miosix::FastInterruptDisableLock;
-using miosix::FastInterruptEnableLock;
-using miosix::Thread;
+#include "drivers/Transceiver.h"
+#include "drivers/Xbee/APIFrameParser.h"
+#include "drivers/spi/SPIDriver.h"
+#include "utils/collections/CircularBuffer.h"
-using miosix::ConditionVariable;
using miosix::FastMutex;
-using miosix::Lock;
-using miosix::Unlock;
-using std::vector;
+#ifndef USE_MOCK_PERIPHERALS
+using GpioType = GpioPin;
+#else
+#include "utils/testutils/MockGpioPin.h"
+using GpioType = MockGpioPin;
+#endif
namespace Xbee
{
-// Constants
+using ParseResult = APIFrameParser::ParseResult;
-// How often should we check if we received a transmit status response.
-static constexpr unsigned int SEND_STATUS_POLL_INTERVAL = 10; // ms
+static constexpr unsigned int FRAME_POLL_INTERVAL = 10; // ms
+static constexpr unsigned int DEFAULT_TX_TIMEOUT = 5000; // ms
-static constexpr uint16_t MAX_PAYLOAD_LEN = 0xFFFF;
+// Size (in frames) of the receive circular buffer
+static constexpr unsigned int RX_FRAMES_BUF_SIZE = 3;
-static constexpr uint8_t START_DELIMITER = 0x7E;
-static constexpr uint64_t BROADCAST_ADDR = 0xFFFF;
-static constexpr uint8_t ID_ACK_DISABLE = 0x00;
-static constexpr uint8_t MAX_BROADCAST_HOPS = 0x00;
-static constexpr uint8_t TX_STATUS_DELIVERY_SUCCESS = 0x00;
+class Xbee : public Transceiver
+{
+public:
+ using OnFrameReceivedListener = std::function<void(APIFrame& frame)>;
-// NOTE: 0x43 = Point-Multipoint, No Ack, No route discovery. See transmit
-// request frame bit options
-static constexpr uint8_t TRANSMIT_OPTIONS = 0x43;
+ /**
+ * @brief Constructs a new instance of the Xbee driver.
+ *
+ * @param bus The SPI bus where the xbee is connected
+ * @param cs Xbee SPI Chip Select Pin
+ * @param attn Xbee ATTN Pin
+ * @param rst Xbee RST PIN
+ * @param tx_timeout How long to wait to for a TX Status
+ */
+ Xbee(SPIBusInterface& bus, GpioType cs, GpioType attn, GpioType rst,
+ long long tx_timeout = DEFAULT_TX_TIMEOUT);
-// Frame Sizes
-static constexpr uint8_t API_HEADER_SIZE = 3;
+ /**
+ * @brief Constructs a new instance of the Xbee driver.
+ *
+ * @param bus The SPI bus where the xbee is connected
+ * @param config Custom SPI bus configuration
+ * @param cs Xbee SPI Chip Select Pin
+ * @param attn Xbee ATTN Pin
+ * @param rst Xbee RST PIN
+ * @param tx_timeout How long to wait to for a TX Status
+ *
+ */
+ Xbee(SPIBusInterface& bus, SPIBusConfig config, GpioType cs, GpioType attn,
+ GpioType rst, long long tx_timeout = DEFAULT_TX_TIMEOUT);
-static constexpr uint8_t TX_FRAME_HEADER_SIZE = 14;
-static constexpr uint8_t RX_FRAME_HEADER_SIZE = 12;
-static constexpr uint8_t CHECKSUM_SIZE = 1;
-static constexpr uint8_t TX_STATUS_FRAME_SIZE = 7;
-static constexpr uint8_t MODEM_STATUS_FRAME_SIZE = 2;
+ ~Xbee();
-// Bit definitions for status frame
-static constexpr uint8_t BIT_STATUS_RETRY_COUNT = 4;
-static constexpr uint8_t BIT_STATUS_DELIVERY = 5;
-static constexpr uint8_t BIT_STATUS_DISCOVERY = 6;
+ /**
+ * @brief Sends a packet.
+ * The function blocks until the packet is sent to the peripheral, but does
+ * not wait for an ACK or send confirmation from the Xbee. Thus, it always
+ * returns true.
+ *
+ * @param pkt Pointer to the packet (needs to be at least pkt_len
+ * bytes).
+ * @param pkt_len Lenght of the packet to be sent.
+ * @return Always true
+ */
+ bool send(uint8_t* pkt, size_t pkt_len) override;
-// Waiting thread to be woken when something has been received or must be sent.
-// Defined extern so it can be accessed from multiple translation units
-extern miosix::Thread* waiting;
+ /**
+ * @brief Waits until a new packet is received.
+ *
+ * @param buf Buffer to store the received packet into.
+ * @param buf_max_size Maximum length of the received data.
+ * @return Size of the data received or -1 if failure
+ */
+ ssize_t receive(uint8_t* buf, size_t buf_max_size) override;
-/**
- * @brief Handler for the ATTN interrupt: wakes up the thread.
- * The ATTN (ATTention Needed) pin is raised when the transceiver has
- * received something.
- * Call this function from inside an IRQHandler, after you have
- * enabled the corresponding interrupt in the NVIC vector.
- */
-static void __attribute__((used)) handleATTNInterrupt()
-{
- if (waiting)
- {
- // Wake
- waiting->IRQwakeup();
+ /**
+ * @brief Hardware resets the Xbee.
+ */
+ void reset();
- if (waiting->IRQgetPriority() >
- miosix::Thread::IRQgetCurrentThread()->IRQgetPriority())
- {
- miosix::Scheduler::IRQfindNextThread();
- }
+ /**
+ * @brief Signals the receive() function that there is new data available.
+ * Call this from the ATTN pin interrupt servirce routine, and nowhere else
+ * plese.
+ *
+ */
+ void handleATTNInterrupt();
- waiting = nullptr;
- }
-}
+ /**
+ * @brief Wakes the receive function without needing an interrupt
+ *
+ * @param force_return Wether receive(..) should return even if it has not
+ * received any data
+ */
+ void wakeReceiver(bool force_return = false);
-/**
- * @warning: An IRQ linked with the ATTN pin of the Xbee module must be enabled
- * before using this class. See test/misc/xbee-bitrate for an example.
- * @warning: Due to the way this driver is written, the SPI bus must be reserved
- * only for the XBee device. No other devices can communicate on the same bus.
- */
-class Xbee : public Transceiver, public ActiveObject
-{
-public:
- Xbee(SPIBusInterface& bus, GpioPin cs, GpioPin attn, GpioPin rst,
- unsigned int send_timeout = 1000)
- : send_timeout(send_timeout), spi_xbee(bus, cs, {}), attn(attn), rst(rst)
- {
- spi_xbee.config.clock_div = SPIClockDivider::DIV128;
-
- // No need to configure before each transaction, we are the only device
- // on the bus.
- spi_xbee.bus.configure(spi_xbee.config);
-
- reset();
- miosix::Thread::sleep(10);
-
- spi_xbee.cs.low();
- miosix::Thread::sleep(1);
- spi_xbee.cs.high();
- miosix::Thread::sleep(1);
- }
-
- Xbee(SPIBusInterface& bus, GpioPin cs, GpioPin attn, GpioPin rst,
- SPIBusConfig spi_config, unsigned int send_timeout = 1000)
- : send_timeout(send_timeout), spi_xbee(bus, cs, spi_config), attn(attn),
- rst(rst)
- {
- // No need to configure before each transaction, we are the only device
- // on the bus.
- spi_xbee.bus.configure(spi_xbee.config);
-
- reset();
- miosix::Thread::sleep(10);
-
- spi_xbee.cs.low();
- miosix::Thread::sleep(1);
- spi_xbee.cs.high();
- miosix::Thread::sleep(1);
- }
-
- /*
- * Send a message through the XBee
- * Blocks until the message is sent (successfully or not)
+ /**
+ * @brief Sends an AT Command to the Xbee (see datasheet) without waiting
+ * for a response
*
- * @param msg Message to be sent
- * @param pkt_len Lenght of the message
- * @return true The message was sent correctly.
- * @return false There was an error sending the message.
+ * @param cmd Two character string with the name of the command (eg "DB")
+ * @param params Optional command parameters
+ * @param params_len Length in bytes of the params array
*/
- bool send(uint8_t* msg, size_t msg_len) override
- {
- if (msg == nullptr || msg_len == 0)
- {
- return false;
- }
-
- // Create a TX API packet
- vector<uint8_t> tx_pkt;
- buildTxPacket(tx_pkt, msg, msg_len);
-
- // Send the packet
- setTxBuf(tx_pkt.data(), tx_pkt.size());
-
- // Wake the runner thread in order to send the data
- wakeThread();
-
- // Wait until the XBee sends the message or a timeout expires
- // It would be cool to have condition variables with timeout to avoid
- // polling...
- unsigned int timeout = 0;
- while (!received_tx_status)
- {
- if (timeout > send_timeout)
- {
- // Timeout. Return error
- TRACE("[Xbee] Send Timeout!\n");
- ++status.tx_timeout_count;
- return false;
- }
- Thread::sleep(SEND_STATUS_POLL_INTERVAL);
- timeout += SEND_STATUS_POLL_INTERVAL;
- }
-
- received_tx_status = false;
- if (status.tx_delivery_status != TX_STATUS_DELIVERY_SUCCESS)
- {
- TRACE("[Xbee] Error: %02X\n", status.tx_delivery_status);
- }
- return status.tx_delivery_status == TX_STATUS_DELIVERY_SUCCESS;
- }
-
- /*
- * Wait for a new message to be received by the XBee
- * @param rcv_buf Buffer to store the received message
- * @param rcv_buf_len Maximum length of the message to be received
- * @return true A message was successfully received
- * @return false Received invalid message
+ void sendATCommand(const char* cmd, uint8_t* params = nullptr,
+ size_t params_len = 0);
+
+ /**
+ * @brief Sends an AT Command to the Xbee and wait for a response (see
+ * datasheet)
+ *
+ * @param cmd Two character string with the name of the command (eg "DB")
+ * @param params Optional command parameters
+ * @param params_len Length in bytes of the params array
+ * @param response Where to store the response
+ * @param timeout Maximum time to wait for the response
+ * @return true if response received before the timeout, false otherwise
*/
- ssize_t receive(uint8_t* rcv_buf, size_t rcv_buf_len) override
- {
- Lock<FastMutex> l(rx_mutex);
-
- while (rx_frame.size() == 0)
- {
- rx_cond.wait(l);
- }
-
- if (rx_frame.size() > rcv_buf_len)
- {
- return -1;
- }
- else
- {
- memcpy(rcv_buf, rx_frame.data(), rx_frame.size());
- rcv_buf_len = rx_frame.size();
-
- rx_frame.clear();
-
- return rcv_buf_len;
- }
- }
-
- void stop() override
- {
- should_stop = true;
- wakeThread();
- ActiveObject::stop();
- }
-
- XbeeStatus getStatus() { return status; }
-
-protected:
+ bool sendATCommand(const char* cmd, ATCommandResponseFrame* response,
+ uint8_t* params = nullptr, size_t params_len = 0,
+ unsigned int timeout = 1000);
+
/**
- * Sends and receives data, puts the thread to sleep if nothing to
- * send/receive.
+ * @brief Set the frame received listener, called each time a new APIFrame
+ * is received from the device
+ *
+ * @param listener The listener
*/
- void run() override
- {
- while (!shouldStop())
- {
- // Wait for RX or TX request
- {
- miosix::FastInterruptDisableLock dLock;
-
- // Check if we have data to send (tx_buf.size > 0) or receive
- // (attn == 0) with disabled interrupts to avoid race conditions
- if (attn.value() != 0 && tx_buf.size() == 0)
- {
- // If we have nothing to receive or send, wait.
- waiting = miosix::Thread::getCurrentThread();
-
- while (waiting != 0)
- {
- waiting->IRQwait();
- {
- miosix::FastInterruptEnableLock eLock(dLock);
- miosix::Thread::yield();
- }
- }
- }
-
- StackLogger::getInstance()->updateStack(THID_XBEE);
- }
- // Transfer any data on the tx buffer and receive any incoming data
- transferData();
- }
- }
+ void setOnFrameReceivedListener(OnFrameReceivedListener listener);
+
+ XbeeStatus getStatus();
private:
- enum class ParseResult : uint8_t
- {
- IDLE,
- PARSING,
- SUCCESS,
- FAIL
- };
-
- enum class ParserState
- {
- FIND_START,
- READ_LENGTH_1,
- READ_LENGTH_2,
- READ_FRAME,
- READ_CHECKSUM
- };
-
- enum FrameType : uint8_t
- {
- FRAMETYPE_AT_COMMAND = 0x08,
- FRAMETYPE_AT_QUEUE_PARAM_VALUE = 0x09,
- FRAMETYPE_TX_REQUEST = 0x10,
- FRAMETYPE_EXPICIT_TX_REQUEST = 0x11,
- FRAMETYPE_REMOTE_AT_REQUEST = 0x17,
- FRAMETYPE_AT_COMMAND_RESPONSE = 0x88,
- FRAMETYPE_MODEM_STATUS = 0x8A,
- FRAMETYPE_TRANSMIT_STATUS = 0x8B,
- FRAMETYPE_ROUTE_INFO_PACKET = 0x8D,
- FRAMETYPE_AGGREGATE_ADRESSING_UPDATE = 0x8E,
- FRAMETYPE_RX_PACKET = 0x90,
- FRAMETYPE_EXPLICIT_RX_PACKET = 0x91,
- FRAMETYPE_DATA_SAMPLE_RX_INDICATOR = 0x92,
- FRAMETYPE_NODE_IDENTIFICATION_INDICATOR = 0x95,
- FRAMETYPE_REMOTE_COMMAND_RESPONSE = 0x97
- };
-
- void reset()
- {
- rst.mode(miosix::Mode::OPEN_DRAIN);
- rst.low();
- miosix::delayUs(500);
- rst.high();
- }
+ /**
+ * @brief Handles an API Frame
+ * @attention mutex_xbee_comm must be locked before calling this function.
+ */
+ void handleFrame(APIFrame& frame);
/**
- * Wake the AO thread from another thread.
- * Cannot be called with disabled interrupts.
+ * @brief Sends an AT command to the devices
+ * @attention mutex_xbee_comm must be locked before calling this function.
*/
- void wakeThread()
- {
- FastInterruptDisableLock dLock;
+ uint8_t sendATCommandInternal(const char* cmd, uint8_t* params = nullptr,
+ size_t params_len = 0);
- if (waiting)
- {
- waiting->IRQwakeup();
- waiting = nullptr;
- }
- }
+ /**
+ * @brief Sends an AT command to the devices
+ * @attention mutex_xbee_comm must be locked before calling this function.
+ */
+ uint8_t sendATCommandInternal(uint8_t frame_id, const char* cmd,
+ uint8_t* params = nullptr,
+ size_t params_len = 0);
+
+ uint8_t buildTXRequestFrame(TXRequestFrame& tx_req, uint8_t* pkt,
+ size_t pkt_len);
/**
- * Transfer data to/from the Xbee.
- * Performs a full duplex transaction.
+ * @brief Fills the provided buffer with data contained in rx_frames_buf
*/
- void transferData()
- {
- SPIBusInterface& bus = spi_xbee.bus;
-
- ParseResult result = ParseResult::IDLE;
-
- bus.select(spi_xbee.cs);
-
- vector<uint8_t> data;
-
- {
- // Make a local copy of the TX buffer
- FastInterruptDisableLock dLock;
- data = tx_buf;
-
- // Clear the tx buffer
- tx_buf.clear();
- }
-
- // If there is data to send
- if (data.size() > 0)
- {
- // Full duplex transfer, the data vector is replaced with received
- // data, if any.
- bus.transfer(data.data(), data.size());
-
- // Parse the received data
- for (uint8_t rx : data)
- {
- result = parse(rx);
-
- // We received something
- if (result == ParseResult::SUCCESS)
- {
- handleFrame(parser_buf);
- }
- }
-
- // If there's nothing more to parse, return
- if (result != ParseResult::PARSING)
- {
- bus.deselect(spi_xbee.cs);
- return;
- }
-
- // If there is more data to be received, continue below.
- }
-
- // Read until we have received a packet (or no packet is found)
- do
- {
- result = parse(bus.read());
- } while (result == ParseResult::PARSING);
-
- if (result == ParseResult::SUCCESS)
- {
- handleFrame(parser_buf);
- }
- else if (result == ParseResult::FAIL)
- {
- TRACE("[Xbee] Read failed. Parser result: %d\n", (int)result);
- }
-
- bus.deselect(spi_xbee.cs);
- }
+ size_t fillReceiveBuf(uint8_t* buf, size_t buf_max_size);
/**
- * Parse received data one byte at a time, storing it in the parser_buf.
- * When a full packet is received, returns ParseResult::SUCCESS.
- * Returns ParseResult::FAIL if a full packet is received but checksum
- * verification fails.
- * In both cases, the frame is stored in parser_buf.
- *
- * Returns ParseResult::IDLE if no frame start delimiter has been found yet.
- * Returns ParseResult::PARSING if a start delimiter was found and a packet
- * is being parsed.
+ * @brief Reads a single frame from the SPI bus.
+ * @attention mutex_xbee_comm must be locked before calling this function.
+ */
+ ParseResult readOneFrame();
+
+ /**
+ * @brief Reads the SPI bus until an RXPacketFrame is encountered or no more
+ * data is available
+ * @attention mutex_xbee_comm must be locked before calling this function.
*
- * @param byte byte to be parsed
- * @return
+ * @return true RXPacketFrame received
+ * @return false No more data available on SPI and RXPacketFrame not
+ * received
*/
- ParseResult parse(uint8_t byte)
- {
- switch (parser_state)
- {
- // Look for the start of frame delimiter
- case ParserState::FIND_START:
- if (byte == START_DELIMITER)
- {
- parser_state = ParserState::READ_LENGTH_1;
-
- // Frame start found, clear old rx buf
- parser_buf.clear();
- parser_packet_length = 0;
- }
- else
- {
- return ParseResult::IDLE;
- }
- break;
- // Read most significant byte of the length
- case ParserState::READ_LENGTH_1:
- parser_packet_length = byte << 8;
- parser_state = ParserState::READ_LENGTH_2;
- break;
- // Read least significant byte of the length
- case ParserState::READ_LENGTH_2:
- parser_packet_length += byte;
-
- parser_state = ParserState::READ_FRAME;
-
- // Now that we know how long the packet is, reserve memory to
- // store it
- parser_buf.reserve(parser_packet_length);
- break;
- // Read the data frame
- case ParserState::READ_FRAME:
- parser_buf.push_back(byte);
-
- if (parser_buf.size() == parser_packet_length)
- {
- parser_state = ParserState::READ_CHECKSUM;
- }
- break;
- // Read & verify checksum
- case ParserState::READ_CHECKSUM:
- parser_state = ParserState::FIND_START;
-
- if (verifyChecksum(parser_buf, byte))
- {
- return ParseResult::SUCCESS;
- }
- else
- {
- ++status.rx_wrong_checksum;
- TRACE("[Xbee] Rx checksum verification failed\n");
- return ParseResult::FAIL;
- }
- break;
- }
-
- return ParseResult::PARSING;
- }
-
- void handleFrame(const vector<uint8_t>& frame)
- {
- if (frame.size() == 0)
- {
- return;
- }
- // The first byte indicates the frame type
- switch (frame[0])
- {
- case FRAMETYPE_RX_PACKET:
- {
- size_t payload_size = frame.size() - RX_FRAME_HEADER_SIZE;
-
- if (payload_size > 0)
- {
- {
- Lock<FastMutex> l(rx_mutex);
- rx_frame.clear();
- rx_frame.insert(rx_frame.end(),
- frame.begin() + RX_FRAME_HEADER_SIZE,
- frame.end());
- }
- rx_cond.signal();
- }
- break;
- }
- case FRAMETYPE_TRANSMIT_STATUS:
- {
- if (frame.size() == TX_STATUS_FRAME_SIZE)
- {
- status.tx_retry_count = frame[BIT_STATUS_RETRY_COUNT];
- status.tx_delivery_status = frame[BIT_STATUS_DELIVERY];
- status.tx_discovery_status = frame[BIT_STATUS_DISCOVERY];
-
- received_tx_status = true;
- }
- else
- {
- TRACE("[Xbee] Wrong TX status frame size.\n");
- }
- break;
- }
- case FRAMETYPE_MODEM_STATUS:
- {
- if (frame.size() == MODEM_STATUS_FRAME_SIZE)
- {
- TRACE("[Xbee] Modem status: %d\n", frame[1]);
- }
- else
- {
- TRACE("[Xbee] Wrong MODEM status frame size.\n");
- }
- break;
- }
- default:
- break;
- }
- }
-
- void buildTxPacket(vector<uint8_t>& tx_pkt, uint8_t* msg, size_t msg_len)
- {
- tx_pkt.reserve(API_HEADER_SIZE + TX_FRAME_HEADER_SIZE + msg_len +
- CHECKSUM_SIZE);
-
- tx_pkt.push_back(START_DELIMITER);
-
- uint16_t frame_len = msg_len + TX_FRAME_HEADER_SIZE;
- // invert pkt_len bytes order
- tx_pkt.push_back((frame_len & 0xff00) >> 8);
- tx_pkt.push_back(frame_len & 0xff);
-
- tx_pkt.push_back(FRAMETYPE_TX_REQUEST);
-
- // Any value != 0 indicates that we want a tx status response
- tx_pkt.push_back(0x01);
-
- // Split the address in bytes & add to the buffer
- uint8_t addr_buffer[8];
- memcpy(addr_buffer, &BROADCAST_ADDR, 8);
-
- for (int i = 7; i >= 0; i--)
- {
- tx_pkt.push_back(addr_buffer[i]);
- }
-
- // Reserved bytes
- tx_pkt.push_back(0xff);
- tx_pkt.push_back(0xfe);
-
- tx_pkt.push_back(MAX_BROADCAST_HOPS);
-
- tx_pkt.push_back(TRANSMIT_OPTIONS);
-
- // payload
- for (uint32_t i = 0; i < msg_len; i++)
- {
- tx_pkt.push_back(*(msg + i));
- }
-
- // Calculate & add checksum
- uint32_t checksum = 0;
- for (uint32_t i = 3; i < tx_pkt.size(); i++)
- {
- checksum += tx_pkt.at(i);
- }
-
- tx_pkt.push_back(0xff - (checksum & 0xff));
- }
+ bool readRXFrame();
/**
- * Set the tx buffer with the provided data.
+ * @brief Writes a frame from the SPI bus.
+ * @attention mutex_xbee_comm must be locked before calling this function.
*/
- void setTxBuf(uint8_t* buf, size_t size)
- {
- // We need to disable interrupts (instead of using a mutex) becouse we
- // need to synchronize the access to the tx buf in the waiting thread
- // before putting it to sleep (which requires disabling interrupts)
- FastInterruptDisableLock dLock;
-
- // Copy buf into tx_buf, removing old content
- tx_buf.clear();
- tx_buf.insert(tx_buf.end(), buf, buf + size);
- }
-
- bool verifyChecksum(vector<uint8_t> frame, uint8_t checksum)
- {
- // Sum all the bytes including checksum.
- // The sum can be stored in a uint8_t since we only care about the least
- // significant bits.
- uint8_t sum = checksum;
- for (size_t i = 0; i < frame.size(); ++i)
- {
- sum += frame[i];
- }
- return sum == 0xFF;
- }
-
- // How long to wait for a transmit status response.
- unsigned int send_timeout;
-
- vector<uint8_t> tx_buf;
-
- bool received_tx_status = false;
-
- FastMutex rx_mutex;
- vector<uint8_t> rx_frame;
- ConditionVariable rx_cond;
-
- ParserState parser_state = ParserState::FIND_START;
- size_t parser_packet_length = 0;
- vector<uint8_t> parser_buf;
+ void writeFrame(APIFrame& frame);
- XbeeStatus status;
+ uint8_t getNewFrameID();
+ /**
+ * @brief Waits until an APIFrame with frame type \p frame_type is received
+ * on the SPI or until the timeouts expires. The frame is then stored in the
+ * parsing_api_frame local class varibale. Any frame received by this
+ * function is automatically passed to the handleFrame() function
+ * @attention mutex_xbee_comm must be locked before calling this function.
+ *
+ * @param poll_interval Polling interval for the attn pin in milliseconds
+ * @param timeout_tick Tick after which the function should stop waiting for
+ * frames
+ * @return true If a frame with type equal to \p frame_type is received
+ * @return false If the timeout_tick reached
+ */
+ bool waitForFrame(uint8_t frame_type, unsigned int poll_interval,
+ long long timeout_tick);
+
+ // Synchronizes all communications to the xbee, as they may happen on
+ // multiple threads
+ FastMutex mutex_xbee_comm;
+
+ APIFrameParser parser;
+ // Frame being parsed. Only valid if last call to
+ // parser.parse() returned ParseResult::SUCCESS
+ APIFrame parsing_api_frame;
+
+ // Temporary storage for RX packet frames, waiting to be returned by
+ // receive()
+ CircularBuffer<RXPacketFrame, RX_FRAMES_BUF_SIZE> rx_frames_buf;
+ FastMutex mutex_rx_frames;
+ // RX Packet currently being returned by receive()
+ RXPacketFrame curr_rx_frame;
+ // Index of the first byte of the payload of curr_rx_frame that will be
+ // returned on the next call to receive()
+ int curr_rx_payload_ptr = -1;
+
+ // SPI defs
SPISlave spi_xbee;
- GpioPin attn;
- GpioPin rst;
-}; // namespace Xbee
+ GpioType attn;
+ GpioType rst;
+
+ // How long to wait for a TX Status
+ long long tx_timeout;
+
+ OnFrameReceivedListener frame_listener;
+
+ // Used to generate a unique frame id
+ uint8_t frame_id_counter = 1;
+
+ // Forces receive() to return even if there is no new data
+ bool force_rcv_return = false;
+
+ // Status structs
+ XbeeStatus status;
+ Stats time_to_send_stats;
+
+ // Waiting thread to be woken when something has been received.
+ miosix::Thread* receive_thread = 0;
+};
} // namespace Xbee
\ No newline at end of file
diff --git a/src/shared/drivers/Xbee/XbeeStatus.h b/src/shared/drivers/Xbee/XbeeStatus.h
index a3bfcaa44..08cb93c7d 100644
--- a/src/shared/drivers/Xbee/XbeeStatus.h
+++ b/src/shared/drivers/Xbee/XbeeStatus.h
@@ -1,17 +1,17 @@
-/*
+/*
* Copyright (c) 2019 Skyward Experimental Rocketry
* Authors: Luca Erbetta
- *
+ *
* 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
@@ -25,27 +25,43 @@
#include <cstdint>
#include <cstdio>
+#include <ostream>
+#include <string>
+#include "math/Stats.h"
namespace Xbee
{
- struct XbeeStatus
+struct XbeeStatus
+{
+ long long timestamp = 0LL;
+
+ uint8_t last_tx_status_error = 0;
+ uint8_t last_tx_status = 0;
+
+ StatsResult time_to_send_stats;
+
+ unsigned int tx_timeout_count = 0;
+
+ unsigned int rx_dropped_buffers = 0;
+
+ unsigned int frame_buf_max_length = 0;
+
+ static std::string header()
+ {
+ return "timestamp,last_tx_status_error,last_tx_status,tts_stats.min,"
+ "tts_stats.max,tts_stats.mean,tts_stats.stddev,tts_stats.n_"
+ "samples,tx_timeout_count,rx_dropped_buffers,frame_buf_max_"
+ "length\n";
+ }
+
+ void print(std::ostream& os) const
{
- uint8_t tx_retry_count = 0;
- uint8_t tx_delivery_status = 0;
- uint8_t tx_discovery_status = 0;
- unsigned int tx_timeout_count = 0;
-
- unsigned int rx_dropped_buffers = 0;
- unsigned int rx_wrong_checksum = 0;
-
- void print()
- {
- TRACE("+++XBEE STATUS+++");
- TRACE("TX: Timeouts: %d\n", tx_timeout_count);
- TRACE("TXSTATUS: Retries: %d, Delivery: %02X, Discovery: %02X\n",
- tx_retry_count, tx_delivery_status, tx_discovery_status);
- TRACE("RX: Dropped: %d, Checksum: %d\n", rx_dropped_buffers, rx_wrong_checksum);
-
- }
- };
-}
\ No newline at end of file
+ os << timestamp << "," << (int)last_tx_status_error << ","
+ << (int)last_tx_status << "," << time_to_send_stats.minValue << ","
+ << time_to_send_stats.maxValue << "," << time_to_send_stats.mean
+ << "," << time_to_send_stats.stdev << ","
+ << time_to_send_stats.nSamples << "," << tx_timeout_count << ","
+ << rx_dropped_buffers << "," << frame_buf_max_length << "\n";
+ }
+};
+} // namespace Xbee
\ No newline at end of file
diff --git a/src/shared/drivers/gps/piksi/piksi.cpp b/src/shared/drivers/gps/piksi/piksi.cpp
index 8e05730f2..be56df4ed 100644
--- a/src/shared/drivers/gps/piksi/piksi.cpp
+++ b/src/shared/drivers/gps/piksi/piksi.cpp
@@ -188,8 +188,9 @@ unsigned int Piksi::lookForMessages(uint8_t *buffer, unsigned int size)
if (messageSize > size)
return consumed; // We don't have the entire message
- uint16_t crc =
- *reinterpret_cast<uint16_t *>(buffer + messageSize - crcSize);
+ uint16_t crc;
+ memcpy(&crc, buffer + messageSize - crcSize, sizeof(crc));
+
if (crc16piksi(buffer + 1, messageSize - crcSize - 1) == crc)
{
processValidMessage(buffer, messageSize);
diff --git a/src/shared/drivers/servo/servo.cpp b/src/shared/drivers/servo/servo.cpp
index d42229285..d45ba9f2e 100644
--- a/src/shared/drivers/servo/servo.cpp
+++ b/src/shared/drivers/servo/servo.cpp
@@ -26,7 +26,7 @@
#include <cstring>
// Initialize the pwm with 50 Hz frequency and 65535 levels of duty cycle
-Servo::Servo(PWM::Timer t) : pwm(t, 50, 65535) { memset(&positions, 0, 4); }
+Servo::Servo(PWM::Timer t) : pwm(t, 50, 65535) { memset(&positions, 0, 4*sizeof(float)); }
Servo::~Servo() {}
diff --git a/src/shared/drivers/spi/SPIBus.h b/src/shared/drivers/spi/SPIBus.h
index e31c42329..c8830353e 100644
--- a/src/shared/drivers/spi/SPIBus.h
+++ b/src/shared/drivers/spi/SPIBus.h
@@ -21,9 +21,19 @@
* THE SOFTWARE.
*/
+#pragma once
+
+#include <cassert>
+
#include "SPIBusInterface.h"
-#pragma once
+
+#ifndef USE_MOCK_PERIPHERALS
+using SPIType = SPI_TypeDef;
+#else
+#include "test/FakeSpiTypedef.h"
+using SPIType = FakeSpiTypedef;
+#endif
/**
* @brief Low level driver for communicating on a SPI Bus, provides
@@ -37,7 +47,7 @@ public:
*
* @param spi Pointer to the SPI peripheral to be used
*/
- SPIBus(SPI_TypeDef* spi);
+ SPIBus(SPIType* spi) : spi(spi) {}
~SPIBus() {}
// Delete copy/move contructors/operators
@@ -87,17 +97,23 @@ public:
/**
* @brief See SPIBusInterface::select()
*/
- void select(GpioPin& cs) override;
+ void select(GpioType& cs) override;
/**
* @brief See SPIBusInterface::deselect()
*/
- void deselect(GpioPin& cs) override;
+ void deselect(GpioType& cs) override;
/**
- * @brief See SPIBusInterface::configure()
+ * @brief Obtains ownership of the bus, configuring it with the provided
+ * config. Since this implementation is not syncronized, if acquire() is
+ * called on an already locked bus, it will:
+ * - FAIL in DEBUG mode
+ * - Do nothing when NOT in DEBUG mode
+ *
+ * Use SyncedSPIBus if you need to synchronize access to the bus.
*/
- void configure(SPIBusConfig config) override;
+ void acquire(SPIBusConfig config) override;
protected:
/**
@@ -122,7 +138,9 @@ protected:
*/
void transfer(uint8_t* byte);
- SPI_TypeDef* spi;
+ void configure(SPIBusConfig new_config);
+
+ SPIType* spi;
SPIBusConfig config{};
bool config_enabled = true;
@@ -171,7 +189,7 @@ inline void SPIBus::transfer(uint8_t* data, size_t size)
}
}
-inline void SPIBus::select(GpioPin& cs)
+inline void SPIBus::select(GpioType& cs)
{
cs.low();
if (config.cs_setup_time_us > 0)
@@ -180,7 +198,7 @@ inline void SPIBus::select(GpioPin& cs)
}
}
-inline void SPIBus::deselect(GpioPin& cs)
+inline void SPIBus::deselect(GpioType& cs)
{
if (config.cs_hold_time_us > 0)
{
@@ -202,7 +220,7 @@ inline void SPIBus::write(uint8_t* byte)
;
// Clear the RX buffer by accessing the DR register
- spi->DR;
+ (void)spi->DR;
}
inline void SPIBus::transfer(uint8_t* byte)
@@ -235,4 +253,51 @@ inline void SPIBus::read(uint8_t* byte)
// Store the received data in the byte
*byte = (uint8_t)spi->DR;
+}
+
+inline void SPIBus::acquire(SPIBusConfig new_config)
+{
+ // Assert that the bus is not already acquired.
+ // This bus is not syncronized: fail if someone tries to take ownership when
+ // the bus is already being used. Use SyncedSPIBus if you need to
+ // synchronize access to the bus
+#ifdef DEBUG
+ assert(isBusy() == false);
+#endif
+
+ SPIBusInterface::acquire(new_config);
+
+ configure(new_config);
+}
+
+inline void SPIBus::configure(SPIBusConfig new_config)
+{
+ // Reconfigure the bus only if config enabled. Do not reconfigure if already
+ // in the correct configuration.
+ if (config_enabled && (!first_config_applied || new_config != config))
+ {
+ first_config_applied = true;
+ config = new_config;
+
+ // Wait until the peripheral is done before changing configuration
+ while ((spi->SR & SPI_SR_TXE) == 0)
+ ;
+ while ((spi->SR & SPI_SR_BSY) > 0)
+ ;
+
+ spi->CR1 = 0;
+
+ // Configure CPOL & CPHA bits
+ spi->CR1 |= static_cast<uint32_t>(config.mode);
+
+ // Configure clock division (BR bits)
+ spi->CR1 |= static_cast<uint32_t>(config.clock_div);
+
+ // Configure LSBFIRST bit
+ spi->CR1 |= static_cast<uint32_t>(config.bit_order);
+
+ spi->CR1 |= SPI_CR1_SSI | SPI_CR1_SSM // Use software chip-select
+ | SPI_CR1_MSTR // Master mode
+ | SPI_CR1_SPE; // Enable SPI
+ }
}
\ No newline at end of file
diff --git a/src/shared/drivers/spi/SPIBusInterface.h b/src/shared/drivers/spi/SPIBusInterface.h
index 9af7e336a..bb313d167 100644
--- a/src/shared/drivers/spi/SPIBusInterface.h
+++ b/src/shared/drivers/spi/SPIBusInterface.h
@@ -31,7 +31,12 @@
using miosix::delayUs;
using miosix::GpioPin;
-class SPITransaction;
+#ifndef USE_MOCK_PERIPHERALS
+using GpioType = GpioPin;
+#else
+#include "utils/testutils/MockGpioPin.h"
+using GpioType = MockGpioPin;
+#endif
/**
* @brief SPI Clock divider.
@@ -118,11 +123,10 @@ struct SPIBusConfig
*/
class SPIBusInterface
{
- friend class SPITransaction;
public:
SPIBusInterface() {}
- ~SPIBusInterface() {}
+ virtual ~SPIBusInterface() {}
// Delete copy/move contructors/operators
SPIBusInterface(const SPIBusInterface&) = delete;
@@ -184,7 +188,7 @@ public:
*
* @param cs Chip select pin for the slave
*/
- virtual void select(GpioPin& cs) = 0;
+ virtual void select(GpioType& cs) = 0;
/**
* @brief Deselects the slave
@@ -192,18 +196,34 @@ public:
* @param cs Chip select pin for the slave
* @return
*/
- virtual void deselect(GpioPin& cs) = 0;
+ virtual void deselect(GpioType& cs) = 0;
/**
* @brief Configures the bus with the provided configuration parameters.
+ * Call this before every transaction, after the call to lock() and before
+ * select(..)
*
* @param config Configuration parameters
* @return
*/
- virtual void configure(SPIBusConfig config) = 0;
+ virtual void acquire(SPIBusConfig config)
+ {
+ (void)config;
+ busy = true;
+ }
+
+ /**
+ * @brief Releases ownership of the bus
+ */
+ virtual void release() { busy = false; }
+
+ /**
+ * @brief Checks wether the bus is currently being used
+ */
+ virtual bool isBusy() { return busy; }
private:
- bool locked = false; // For use by SPITransaction
+ bool busy = false; // For use by SPITransaction
};
/**
@@ -215,10 +235,51 @@ struct SPISlave
SPIBusConfig config; ///> How the bus should be configured to communicate
///> with the slave.
- GpioPin cs; ///> Chip select pin
+ GpioType cs; ///> Chip select pin
- SPISlave(SPIBusInterface& bus, GpioPin cs, SPIBusConfig config)
+ SPISlave(SPIBusInterface& bus, GpioType cs, SPIBusConfig config)
: bus(bus), config(config), cs(cs)
{
}
};
+
+/**
+ * @brief RAII Interface for SPI bus acquisition
+ *
+ */
+class SPIAcquireLock
+{
+public:
+ SPIAcquireLock(SPISlave slave) : SPIAcquireLock(slave.bus, slave.config) {}
+
+ SPIAcquireLock(SPIBusInterface& bus, SPIBusConfig cfg) : bus(bus)
+ {
+ bus.acquire(cfg);
+ }
+
+ ~SPIAcquireLock() { bus.release(); }
+
+private:
+ SPIBusInterface& bus;
+};
+
+/**
+ * @brief RAII Interface for SPI chip selection
+ *
+ */
+class SPISelectLock
+{
+public:
+ SPISelectLock(SPISlave slave) : SPISelectLock(slave.bus, slave.cs) {}
+
+ SPISelectLock(SPIBusInterface& bus, GpioType cs) : bus(bus), cs(cs)
+ {
+ bus.select(cs);
+ }
+
+ ~SPISelectLock() { bus.deselect(cs); }
+
+private:
+ SPIBusInterface& bus;
+ GpioType& cs;
+};
\ No newline at end of file
diff --git a/src/shared/drivers/spi/SPITransaction.cpp b/src/shared/drivers/spi/SPITransaction.cpp
index de43334f0..b0bc6dfc8 100644
--- a/src/shared/drivers/spi/SPITransaction.cpp
+++ b/src/shared/drivers/spi/SPITransaction.cpp
@@ -23,29 +23,19 @@
#include "SPITransaction.h"
-#include <cassert>
-
SPITransaction::SPITransaction(SPISlave slave)
: SPITransaction(slave.bus, slave.cs, slave.config)
{
}
-SPITransaction::SPITransaction(SPIBusInterface& bus, GpioPin cs,
+SPITransaction::SPITransaction(SPIBusInterface& bus, GpioType cs,
SPIBusConfig config)
: bus(bus), cs(cs)
{
- // Only one SPITransaction may be active at any given time.
- // Do not store an instance of SPITransaction for a long time! Create one,
- // use it, and destroy it as soon as you are done operating on the bus!
- // (just like mutexes)
-#ifdef DEBUG
- assert(bus.locked == false);
-#endif
- bus.locked = true;
- bus.configure(config);
+ bus.acquire(config);
}
-SPITransaction::~SPITransaction() { bus.locked = false; }
+SPITransaction::~SPITransaction() { bus.release(); }
void SPITransaction::write(uint8_t cmd)
{
diff --git a/src/shared/drivers/spi/SPITransaction.h b/src/shared/drivers/spi/SPITransaction.h
index b75c20d4a..3ede19959 100644
--- a/src/shared/drivers/spi/SPITransaction.h
+++ b/src/shared/drivers/spi/SPITransaction.h
@@ -71,7 +71,7 @@ public:
* @param cs Chip select of the slave to communicate to
* @param config Configuration of the bus for the selected slave
*/
- SPITransaction(SPIBusInterface& bus, GpioPin cs, SPIBusConfig config);
+ SPITransaction(SPIBusInterface& bus, GpioType cs, SPIBusConfig config);
~SPITransaction();
@@ -161,5 +161,5 @@ public:
private:
SPIBusInterface& bus;
- GpioPin cs;
+ GpioType cs;
};
\ No newline at end of file
diff --git a/src/shared/drivers/spi/SPIBus.cpp b/src/shared/drivers/spi/SyncedSPIBus.h
similarity index 53%
rename from src/shared/drivers/spi/SPIBus.cpp
rename to src/shared/drivers/spi/SyncedSPIBus.h
index a28324ce1..e71f77b14 100644
--- a/src/shared/drivers/spi/SPIBus.cpp
+++ b/src/shared/drivers/spi/SyncedSPIBus.h
@@ -21,38 +21,31 @@
* THE SOFTWARE.
*/
+#pragma once
+
#include "SPIBus.h"
+#include <miosix.h>
-SPIBus::SPIBus(SPI_TypeDef* spi) : spi(spi) {}
+using miosix::FastMutex;
-void SPIBus::configure(SPIBusConfig new_config)
+/**
+ * @brief Extension of SPIBus to sync access to the bus between multiple threads
+ */
+class SyncedSPIBus : public SPIBus
{
- // Reconfigure the bus only if config enabled. Do not reconfigure if already
- // in the correct configuration.
- if (config_enabled && (!first_config_applied || new_config != config))
+ virtual void acquire(SPIBusConfig config) override
{
- first_config_applied = true;
- config = new_config;
-
- // Wait until the peripheral is done before changing configuration
- while (!(spi->SR & SPI_SR_TXE))
- ;
- while ((spi->SR & SPI_SR_BSY))
- ;
-
- spi->CR1 = 0;
+ mutex.lock();
+ SPIBusInterface::acquire(config);
- // Configure CPOL & CPHA bits
- spi->CR1 |= static_cast<uint32_t>(config.mode);
-
- // Configure clock division (BR bits)
- spi->CR1 |= static_cast<uint32_t>(config.clock_div);
-
- // Configure LSBFIRST bit
- spi->CR1 |= static_cast<uint32_t>(config.bit_order);
+ configure(config);
+ }
- spi->CR1 |= SPI_CR1_SSI | SPI_CR1_SSM // Use software chip-select
- | SPI_CR1_MSTR // Master mode
- | SPI_CR1_SPE; // Enable SPI
+ virtual void release() override
+ {
+ SPIBusInterface::release();
+ mutex.unlock();
}
-}
\ No newline at end of file
+private:
+ FastMutex mutex;
+};
\ No newline at end of file
diff --git a/src/tests/catch/spidriver/FakeSpiTypedef.h b/src/shared/drivers/spi/test/FakeSpiTypedef.h
similarity index 89%
rename from src/tests/catch/spidriver/FakeSpiTypedef.h
rename to src/shared/drivers/spi/test/FakeSpiTypedef.h
index dab7a26fa..433a69f8a 100644
--- a/src/tests/catch/spidriver/FakeSpiTypedef.h
+++ b/src/shared/drivers/spi/test/FakeSpiTypedef.h
@@ -26,24 +26,10 @@
#include <cstdint>
#include <vector>
+#include "utils/testutils/MockGpioPin.h"
using std::vector;
-class FakeGpioPin : public miosix::GpioPin
-{
-public:
- FakeGpioPin() : GpioPin(GPIOA_BASE,1) {}
-
- void high() { val = 1; }
-
- void low() { val = 0; }
-
- int value() { return val; }
-
-private:
- int val = 1;
-};
-
/**
* @brief Mock STM32F4 SPI peripheral: intercepts register value changes to
* emulate a real SPI peripheral / slave.
@@ -93,7 +79,7 @@ struct FakeSpiTypedef
uint32_t CR2_expected = 0;
RegDR DR;
- FakeGpioPin cs;
+ MockGpioPin cs;
- FakeSpiTypedef() : DR(*this) {}
+ FakeSpiTypedef() : DR(*this) {cs.high();}
};
\ No newline at end of file
diff --git a/src/shared/drivers/spi/MockSPIBus.h b/src/shared/drivers/spi/test/MockSPIBus.h
similarity index 52%
rename from src/shared/drivers/spi/MockSPIBus.h
rename to src/shared/drivers/spi/test/MockSPIBus.h
index d3436ffe5..8bb7aee7c 100644
--- a/src/shared/drivers/spi/MockSPIBus.h
+++ b/src/shared/drivers/spi/test/MockSPIBus.h
@@ -24,9 +24,10 @@
#pragma once
#include <cstdint>
+#include <cstdio>
#include <vector>
-#include "SPIBusInterface.h"
+#include "../SPIBusInterface.h"
using std::vector;
@@ -47,10 +48,23 @@ using std::vector;
* 5. ???
* 6. Profit.
*/
+
+#ifndef USE_MOCK_PERIPHERALS
+#error \
+ "SpiBusInterface must be built using MockGpioPin (-DUSE_MOCK_PERIPHERALS)"
+#endif
+
+#include "utils/testutils/MockGpioPin.h"
+
+using miosix::FastMutex;
+using miosix::Lock;
class MockSPIBus : public SPIBusInterface
{
public:
- MockSPIBus() {}
+ MockSPIBus(SPIBusConfig expected_config) : expected_config(expected_config)
+ {
+ }
+
~MockSPIBus() {}
// Delete copy/move contructors/operators
@@ -63,17 +77,17 @@ public:
/**
* @brief See SPIBusInterface::write()
*/
- void write(uint8_t byte) override;
+ virtual void write(uint8_t byte) override;
/**
* @brief See SPIBusInterface::write()
*/
- void write(uint8_t* data, size_t size) override;
+ virtual void write(uint8_t* data, size_t size) override;
/**
* @brief See SPIBusInterface::read()
*/
- uint8_t read() override;
+ virtual uint8_t read() override;
/**
* @brief See SPIBusInterface::read()
@@ -83,57 +97,100 @@ public:
/**
* @brief See SPIBusInterface::transfer()
*/
- uint8_t transfer(uint8_t data) override;
+ virtual uint8_t transfer(uint8_t data) override;
/**
* @brief See SPIBusInterface::transfer()
*/
- void transfer(uint8_t* data, size_t size) override;
+ virtual void transfer(uint8_t* data, size_t size) override;
/**
* @brief See SPIBusInterface::select()
*
- * @param cs Not used, pass any GpioPin
*/
- void select(GpioPin& cs) override;
+ virtual void select(GpioType& cs) override;
/**
* @brief See SPIBusInterface::deselect()
*
- * @param cs Not used, pass any GpioPin
*/
- void deselect(GpioPin& cs) override;
+ virtual void deselect(GpioType& cs) override;
/**
- * @brief See SPIBusInterface::configure()
+ * @brief See SPIBusInterface::acquire()
*/
- void configure(SPIBusConfig config) override;
+ virtual void acquire(SPIBusConfig config) override;
/**
* @brief Wether the chip select is asserted or not
*/
- bool isSelected() { return selected; }
+ virtual bool isSelected()
+ {
+
+ Lock<FastMutex> l(mutex);
+ return selected;
+ }
+
+ virtual vector<uint8_t> getOutBuf()
+ {
+ Lock<FastMutex> l(mutex);
+ return out_buf;
+ }
+
+ virtual vector<uint8_t> getInBuf()
+ {
+ Lock<FastMutex> l(mutex);
+ return in_buf;
+ }
+
+ virtual void push(uint8_t* data, size_t len);
+
+ virtual void clearBuffers()
+ {
+ out_buf.clear();
+ in_buf.clear();
+ in_buf_pos_cntr = 0;
+ }
+
+protected:
+ FastMutex mutex;
vector<uint8_t> out_buf; // Data written on the bus are stored here
vector<uint8_t> in_buf; // Store here data to be read from the bus
-
- unsigned int in_buf_pos = 0; // Read data iterator
+ size_t in_buf_pos_cntr = 0;
SPIBusConfig expected_config; // Expected configuration of the bus
-private:
+ virtual uint8_t _read();
+ virtual void _write(uint8_t);
+
+ virtual void _push(uint8_t* data, size_t len);
+
bool canCommunicate();
SPIBusConfig current_config;
bool selected = false;
};
-bool MockSPIBus::canCommunicate()
+inline bool MockSPIBus::canCommunicate()
{
- return selected && current_config == expected_config;
+ bool result = selected && current_config == expected_config;
+ if (!result)
+ {
+ printf("Error, cannot communicato on MockSPIBus: ");
+ if (!selected)
+ {
+ printf("Chip select not asserted\n");
+ }
+ else
+ {
+ printf("Incorrect configuration\n");
+ }
+ }
+ return result;
}
-void MockSPIBus::write(uint8_t byte)
+inline void MockSPIBus::_write(uint8_t byte)
{
if (canCommunicate())
{
@@ -145,95 +202,97 @@ void MockSPIBus::write(uint8_t byte)
}
}
-void MockSPIBus::write(uint8_t* data, size_t size)
+inline void MockSPIBus::write(uint8_t byte)
{
- if (canCommunicate())
- {
- out_buf.insert(out_buf.end(), data, data + size);
- }
- else
- {
- out_buf.insert(out_buf.end(), size, 0);
- }
+ Lock<FastMutex> l(mutex);
+ _write(byte);
}
-uint8_t MockSPIBus::read()
+inline void MockSPIBus::write(uint8_t* data, size_t size)
{
- if (canCommunicate())
+ Lock<FastMutex> l(mutex);
+
+ for (size_t i = 0; i < size; i++)
{
- return in_buf[in_buf_pos++];
+ _write(data[i]);
}
- return 0;
}
-void MockSPIBus::read(uint8_t* data, size_t size)
+inline uint8_t MockSPIBus::read()
{
- if (canCommunicate())
- {
- for (size_t i = 0; i < size; i++)
- {
- *data = in_buf[in_buf_pos++];
- data++;
- }
- }
- else
- {
- for (size_t i = 0; i < size; i++)
- {
- *data = 0;
- data++;
- }
- }
+ Lock<FastMutex> l(mutex);
+ return _read();
}
-uint8_t MockSPIBus::transfer(uint8_t data)
+inline void MockSPIBus::read(uint8_t* data, size_t size)
{
- if (canCommunicate())
+ Lock<FastMutex> l(mutex);
+ for (size_t i = 0; i < size; i++)
{
-
- out_buf.push_back(data);
- return in_buf[in_buf_pos++];
- }
- else
- {
-
- out_buf.push_back(0);
- return 0;
+ *data = _read();
+ data++;
}
}
-void MockSPIBus::transfer(uint8_t* data, size_t size)
+inline uint8_t MockSPIBus::_read()
{
if (canCommunicate())
{
- for (size_t i = 0; i < size; i++)
+ if (in_buf_pos_cntr < in_buf.size())
{
- out_buf.push_back(*data);
- *data = in_buf[in_buf_pos++];
- data++;
+ return in_buf[in_buf_pos_cntr++];
}
}
- else
+ return 0;
+}
+
+inline uint8_t MockSPIBus::transfer(uint8_t data)
+{
+ Lock<FastMutex> l(mutex);
+ _write(data);
+ return _read();
+}
+
+inline void MockSPIBus::transfer(uint8_t* data, size_t size)
+{
+ Lock<FastMutex> l(mutex);
+ for (size_t i = 0; i < size; i++)
{
- for (size_t i = 0; i < size; i++)
- {
- out_buf.push_back(0);
- *data = 0;
- data++;
- }
+ _write(data[i]);
+ data[i] = _read();
}
}
-void MockSPIBus::select(GpioPin& cs)
+inline void MockSPIBus::select(GpioType& cs)
{
- (void)cs;
+ Lock<FastMutex> l(mutex);
+ cs.low();
selected = true;
}
-void MockSPIBus::deselect(GpioPin& cs)
+inline void MockSPIBus::deselect(GpioType& cs)
{
- (void)cs;
+ Lock<FastMutex> l(mutex);
+ cs.high();
selected = false;
}
-void MockSPIBus::configure(SPIBusConfig config) { current_config = config; }
\ No newline at end of file
+inline void MockSPIBus::acquire(SPIBusConfig config)
+{
+ Lock<FastMutex> l(mutex);
+
+ SPIBusInterface::acquire(config);
+
+ current_config = config;
+}
+
+inline void MockSPIBus::push(uint8_t* data, size_t len)
+{
+ Lock<FastMutex> l(mutex);
+ _push(data, len);
+}
+
+inline void MockSPIBus::_push(uint8_t* data, size_t len)
+{
+ in_buf.insert(in_buf.end(), data, data + len);
+}
\ No newline at end of file
diff --git a/src/shared/logger/Deserializer.h b/src/shared/logger/Deserializer.h
index aa673fdaa..7c1193f86 100644
--- a/src/shared/logger/Deserializer.h
+++ b/src/shared/logger/Deserializer.h
@@ -83,7 +83,7 @@ public:
return false;
}
- char c_filename[64];
+ char c_filename[128];
sprintf(c_filename, "%s%s_%s.csv", prefix.c_str(), logFile.c_str(),
typeid(T).name());
diff --git a/src/shared/logger/Logger.cpp b/src/shared/logger/Logger.cpp
index afa1d7656..2a4e6e789 100644
--- a/src/shared/logger/Logger.cpp
+++ b/src/shared/logger/Logger.cpp
@@ -26,16 +26,19 @@
***************************************************************************/
#include "Logger.h"
+
+#include <errno.h>
#include <fcntl.h>
#include <interfaces/atomic_ops.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <tscpp/buffer.h>
+
#include <stdexcept>
+
#include "Debug.h"
#include "diagnostic/SkywardStack.h"
#include "diagnostic/StackLogger.h"
-#include <errno.h>
using namespace std;
using namespace miosix;
@@ -74,7 +77,10 @@ int Logger::start()
file = fopen(filename.c_str(), "ab");
if (file == NULL)
+ {
+ fileNumber = -1;
throw runtime_error("Error opening log file");
+ }
setbuf(file, NULL);
// The boring part, start threads one by one and if they fail, undo
diff --git a/src/shared/logger/Logger.h b/src/shared/logger/Logger.h
index 0794e0fa6..1c313805a 100644
--- a/src/shared/logger/Logger.h
+++ b/src/shared/logger/Logger.h
@@ -28,11 +28,13 @@
#pragma once
#include <miosix.h>
+
#include <cstdio>
#include <list>
#include <queue>
#include <string>
#include <type_traits>
+
#include "LogStats.h"
using std::string;
@@ -101,9 +103,9 @@ public:
}
/**
- * Returns current log filename
- * @return
- */
+ * Returns current log filename
+ * @return
+ */
string getFileName() { return getFileName(fileNumber); }
LogStats getLogStats() { return s; }
@@ -129,7 +131,10 @@ public:
template <typename T>
LogResult log(const T &t)
{
- // static_assert(std::is_trivially_copyable<T>::value,"");
+ static_assert(
+ std::is_trivially_copyable<T>::value,
+ "A type T must be trivially copyable in order to be logged!");
+
return logImpl(typeid(t).name(), &t, sizeof(t));
}
@@ -176,8 +181,8 @@ private:
static const unsigned int filenameMaxRetry =
100; ///< Limit on new filename
- static const unsigned int maxRecordSize = 256; ///< Limit on logged data
- static const unsigned int numRecords = 1024; ///< Size of record queues
+ static const unsigned int maxRecordSize = 512; ///< Limit on logged data
+ static const unsigned int numRecords = 512; ///< Size of record queues
static const unsigned int bufferSize = 64 * 1024; ///< Size of each buffer
static const unsigned int numBuffers = 8; ///< Number of buffers
diff --git a/src/shared/math/Stats.cpp b/src/shared/math/Stats.cpp
index 3b3567a71..43231d8f2 100644
--- a/src/shared/math/Stats.cpp
+++ b/src/shared/math/Stats.cpp
@@ -42,7 +42,7 @@ ostream& operator<<(ostream& os, const StatsResult& sr)
Stats::Stats()
: minValue(numeric_limits<float>::max()),
- maxValue(numeric_limits<float>::min()), mean(0.f), m2(0.f), n(0)
+ maxValue(numeric_limits<float>::lowest()), mean(0.f), m2(0.f), n(0)
{
}
@@ -64,7 +64,7 @@ void Stats::add(float data)
void Stats::reset()
{
minValue = numeric_limits<float>::max();
- maxValue = numeric_limits<float>::min();
+ maxValue = numeric_limits<float>::lowest();
mean = 0.f;
m2 = 0.f;
n = 0;
diff --git a/src/shared/sensors/MS580301BA07/MS580301BA07.h b/src/shared/sensors/MS580301BA07/MS580301BA07.h
index efc5e3618..4aefbee2a 100644
--- a/src/shared/sensors/MS580301BA07/MS580301BA07.h
+++ b/src/shared/sensors/MS580301BA07/MS580301BA07.h
@@ -123,7 +123,7 @@ public:
case STATE_INIT:
spi.write(CONVERT_D1_4096);
mStatus = STATE_SAMPLED_PRESSURE;
-
+ break;
case STATE_SAMPLED_PRESSURE:
spi.read(ADC_READ, rcvbuf, 3, false);
mInternalPressure = rcvbuf[2] | ((uint32_t)rcvbuf[1] << 8) |
diff --git a/src/shared/utils/gui/GridLayout.h b/src/shared/utils/gui/GridLayout.h
new file mode 100644
index 000000000..d5bd54418
--- /dev/null
+++ b/src/shared/utils/gui/GridLayout.h
@@ -0,0 +1,209 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <mxgui/misc_inst.h>
+
+#include <map>
+
+#include "View.h"
+
+/**
+ * @brief Displays childs in a num_rows*num_cols grid
+ */
+class GridLayout : public View
+{
+public:
+ using GridPosition = std::pair<uint8_t, uint8_t>;
+
+ /**
+ * @brief Creates a new GridLayout
+ *
+ * @param num_rows Number of rows
+ * @param num_cols Number of columns
+ * @param spacing Distance in pixels between each cell
+ */
+ GridLayout(uint8_t num_rows, uint8_t num_cols, short int spacing = 0)
+ : View(), num_rows(num_rows), num_cols(num_cols), spacing(spacing)
+ {
+ }
+
+ virtual ~GridLayout() {}
+
+ void setCell(View* child, unsigned int position)
+ {
+ uint8_t col = position % num_cols;
+ uint8_t row = (position - col) / num_cols;
+
+ setCell(child, row, col);
+ }
+
+ void setCell(View* child, uint8_t row, uint8_t col)
+ {
+ if (row >= num_rows || col >= num_cols)
+ {
+ return;
+ }
+
+ map_childs[GridPosition(row, col)] = child;
+
+ updateChildBounds();
+ invalidate();
+ }
+
+ View* getCell(uint8_t row, uint8_t col)
+ {
+ GridPosition pos(row, col);
+
+ if (map_childs.count(pos) > 0)
+ return map_childs[pos];
+ else
+ return nullptr;
+ }
+
+ View* getCell(unsigned int position)
+ {
+ uint8_t col = position % num_cols;
+ uint8_t row = position - col / num_rows;
+
+ return getCell(row, col);
+ }
+
+ void clearCell(View* child)
+ {
+ for (auto it = map_childs.begin(); it != map_childs.end(); it++)
+ {
+ if (it->second == child)
+ {
+ map_childs.erase(it);
+ break;
+ }
+ }
+
+ invalidate();
+ }
+
+ /**
+ * @brief Wether to draw the borders of each cell or not
+ *
+ * @param draw_border True: draw the border
+ * @param color Border color
+ */
+ void setDrawBorder(bool draw_border, mxgui::Color color = mxgui::white)
+ {
+ this->draw_border = draw_border;
+ border_color = color;
+
+ invalidate();
+ }
+
+ void setBounds(Bounds bounds) override
+ {
+ View::setBounds(bounds);
+
+ updateChildBounds();
+ }
+
+ virtual void draw(mxgui::DrawingContext& context) override
+ {
+ View::draw(context);
+
+ for (uint8_t row = 0; row < num_rows; ++row)
+ {
+ for (uint8_t col = 0; col < num_cols; ++col)
+ {
+ GridPosition pos(row, col);
+
+ bool child_selected = false;
+ if (map_childs.count(pos) > 0)
+ {
+ map_childs[pos]->draw(context);
+ child_selected = map_childs[pos]->isSelected();
+ }
+
+ if (draw_border && !child_selected)
+ {
+ context.drawRectangle(
+ map_child_bounds[pos].topLeft(),
+ map_child_bounds[pos].bottomRight(), border_color);
+ }
+ }
+ }
+ }
+
+ uint8_t getRows() { return num_rows; }
+
+ uint8_t getCols() { return num_cols; }
+
+ std::vector<View*> getChilds() override
+ {
+ std::vector<View*> out;
+ for (auto it = map_childs.begin(); it != map_childs.end(); it++)
+ {
+ out.push_back(it->second);
+ }
+ return out;
+ }
+
+private:
+ void updateChildBounds()
+ {
+ Bounds bounds = getBounds();
+ Bounds child_bounds;
+
+ child_bounds.size.width = std::max(
+ 0, (bounds.size.width - (num_cols + 1) * spacing) / num_cols);
+ child_bounds.size.height = std::max(
+ 0, (bounds.size.height - (num_rows + 1) * spacing) / num_rows);
+
+ for (uint8_t row = 0; row < num_rows; ++row)
+ {
+ for (uint8_t col = 0; col < num_cols; ++col)
+ {
+ GridPosition pos(row, col);
+
+ child_bounds.pos.x = bounds.pos.x + (col + 1) * spacing +
+ col * child_bounds.size.width;
+ child_bounds.pos.y = bounds.pos.y + (row + 1) * spacing +
+ row * child_bounds.size.height;
+
+ map_child_bounds[pos] = child_bounds;
+ if (map_childs.count(pos) > 0)
+ {
+ map_childs[pos]->setBounds(child_bounds);
+ }
+ }
+ }
+ }
+
+ uint8_t num_rows;
+ uint8_t num_cols;
+ short int spacing;
+
+ bool draw_border = false;
+ mxgui::Color border_color = mxgui::white;
+
+ std::map<GridPosition, View*> map_childs;
+ std::map<GridPosition, Bounds> map_child_bounds;
+};
\ No newline at end of file
diff --git a/src/shared/utils/gui/ImageView.h b/src/shared/utils/gui/ImageView.h
new file mode 100644
index 000000000..9ecdc37bb
--- /dev/null
+++ b/src/shared/utils/gui/ImageView.h
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <mxgui/image.h>
+
+#include "View.h"
+
+/**
+ * @brief Simple view that displays an image
+ */
+class ImageView : public View
+{
+public:
+ ImageView(const mxgui::Image* image) : image(image) {}
+
+ void setImage(mxgui::Image* image)
+ {
+ this->image = image;
+ invalidate();
+ }
+
+ const mxgui::Image* getImage() { return image; }
+
+ void draw(mxgui::DrawingContext& dc) override
+ {
+ if (isInvalidated())
+ {
+ View::draw(dc);
+
+ dc.clippedDrawImage(getBounds().topLeft(), getBounds().topLeft(),
+ getBounds().bottomRight(), *image);
+ }
+ }
+private:
+ const mxgui::Image* image;
+};
\ No newline at end of file
diff --git a/src/shared/utils/gui/Misc.h b/src/shared/utils/gui/Misc.h
new file mode 100644
index 000000000..0576c5afa
--- /dev/null
+++ b/src/shared/utils/gui/Misc.h
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+#include <mxgui/display.h>
+
+/**
+ * @brief Converts a 24bit color to a 16bit color
+ *
+ * @param rgb 24 bit color
+ * @return Color Closest 16 bit color
+ */
+mxgui::Color color24to16(uint32_t rgb)
+{
+ uint8_t r, g, b;
+ r = ((rgb >> 2) & 0xFF) >> 3;
+ g = ((rgb >> 1) & 0xFF) >> 3;
+ b = (rgb & 0xFF) >> 3;
+ return ((b << 10) | (g << 5) | (r));
+}
\ No newline at end of file
diff --git a/src/shared/utils/gui/NavController.h b/src/shared/utils/gui/NavController.h
new file mode 100644
index 000000000..7cd611ace
--- /dev/null
+++ b/src/shared/utils/gui/NavController.h
@@ -0,0 +1,148 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include "Debug.h"
+#include "TextView.h"
+#include "View.h"
+#include "utils/ButtonHandler.h"
+
+/**
+ * @brief UI navigation controller: listens for button clicks and dispatches the
+ * interactions to the view tree.
+ */
+class NavController
+{
+public:
+ NavController() {}
+
+ /**
+ * @brief
+ *
+ * @param root
+ */
+ void updateViewTree(View* root)
+ {
+ if (selected_index < vec_selectable.size())
+ {
+ vec_selectable.at(selected_index)->setSelected(false);
+ }
+
+ vec_selectable.clear();
+ selected_index = 0;
+
+ updateSelectableViews(root);
+
+ if (vec_selectable.size() > 0)
+ {
+ vec_selectable.at(0)->setSelected(true);
+ }
+ }
+
+ void onButtonPress(ButtonPress press)
+ {
+ switch (press)
+ {
+ case ButtonPress::SHORT:
+ if (vec_selectable.size() > 0)
+ {
+ selectNext();
+ }
+ break;
+ case ButtonPress::DOWN:
+ if (selected_index < vec_selectable.size())
+ {
+ vec_selectable.at(selected_index)
+ ->performInteraction(Interaction::BTN_DOWN);
+ }
+ break;
+ case ButtonPress::UP:
+ if (selected_index < vec_selectable.size())
+ {
+ vec_selectable.at(selected_index)
+ ->performInteraction(Interaction::BTN_UP);
+ }
+ break;
+ case ButtonPress::LONG:
+ if (selected_index < vec_selectable.size())
+ {
+ vec_selectable.at(selected_index)
+ ->performInteraction(Interaction::CLICK);
+ }
+ break;
+ case ButtonPress::VERY_LONG:
+ if (selected_index < vec_selectable.size())
+ {
+ vec_selectable.at(selected_index)
+ ->performInteraction(Interaction::LONG_CLICK);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+private:
+ void selectNext()
+ {
+ // Deselect old drawble
+ if (selected_index < vec_selectable.size())
+ {
+ vec_selectable.at(selected_index)->setSelected(false);
+ }
+
+ if (vec_selectable.size() > 0)
+ {
+ selected_index = (selected_index + 1) % vec_selectable.size();
+
+ vec_selectable.at(selected_index)->setSelected(true);
+
+ TextView* text =
+ dynamic_cast<TextView*>(vec_selectable.at(selected_index));
+
+ if (text)
+ {
+ TRACE("[NavController] %s\n", text->getText().c_str());
+ }
+ }
+ }
+
+ void updateSelectableViews(View* root)
+ {
+ std::vector<View*> childs = root->getChilds();
+ for (auto child : childs)
+ {
+ if (child->isSelectable())
+ {
+ vec_selectable.push_back(child);
+ }
+ updateSelectableViews(child);
+ }
+ }
+
+ unsigned int selected_index = 0;
+ std::vector<View*> vec_selectable;
+};
\ No newline at end of file
diff --git a/src/shared/utils/gui/OptionView.h b/src/shared/utils/gui/OptionView.h
new file mode 100644
index 000000000..9dbef9b77
--- /dev/null
+++ b/src/shared/utils/gui/OptionView.h
@@ -0,0 +1,170 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <map>
+#include <string>
+
+#include "GridLayout.h"
+#include "TextView.h"
+
+/**
+ * @brief View used to display an option list, so the user can select one by
+ * clicking on it.
+ */
+class OptionView : public View
+{
+public:
+ using OnOptionChosenListener = std::function<void(unsigned int id)>;
+
+ OptionView(std::string name, std::map<uint8_t, std::string> options,
+ uint8_t default_option = 0, uint8_t num_cols = 4)
+ : tv_title(new TextView(name))
+ {
+ uint8_t num_rows = options.size() / num_cols;
+ if (options.size() % num_cols != 0)
+ {
+ ++num_rows;
+ }
+
+ grid_options = new GridLayout(num_rows, num_cols);
+
+ unsigned int grid_pos = 0;
+ unsigned int i = 0;
+ for (auto it = options.begin(); it != options.end(); it++)
+ {
+ using namespace std::placeholders;
+
+ TextView* tv_opt = new TextView(it->second);
+ tv_opt->setSelectable(true);
+ tv_opt->addOnInteractionListener(
+ std::bind(&OptionView::onOptionClick, this, _1, _2));
+ tv_opt->setAlignment(HorizAlignment::CENTER, VertAlignment::CENTER);
+
+ map_options[tv_opt] = it->first;
+
+ grid_options->setCell(tv_opt, grid_pos++);
+
+ if (i == default_option)
+ {
+ selectOption(tv_opt);
+ }
+
+ ++i;
+ }
+ }
+
+ virtual ~OptionView()
+ {
+ delete grid_options;
+ delete tv_title;
+ for (auto it = map_options.begin(); it != map_options.end(); it++)
+ {
+ delete it->first;
+ }
+ }
+
+ void setBounds(Bounds bounds) override
+ {
+ View::setBounds(bounds);
+
+ Bounds title_bounds = getBounds();
+ title_bounds.size.height = tv_title->getFont().getHeight();
+ tv_title->setBounds(title_bounds);
+
+ Bounds grid_bounds = getBounds();
+ grid_bounds.pos.y = getBounds().pos.y + title_bounds.size.height + 1;
+ grid_bounds.size.height =
+ getBounds().size.height - title_bounds.size.height;
+
+ grid_options->setBounds(grid_bounds);
+ }
+
+ /**
+ * @brief Adds a callback to be called each time an option is selected by
+ * the user
+ */
+ void addOnOptionChosenListener(OnOptionChosenListener listener)
+ {
+ opt_listeners.push_back(listener);
+ }
+
+ std::vector<View*> getChilds() override
+ {
+ std::vector<View*> out;
+ out.push_back(tv_title);
+ out.push_back(grid_options);
+
+ return out;
+ }
+
+ void draw(mxgui::DrawingContext& dc) override
+ {
+ View::draw(dc);
+ tv_title->draw(dc);
+ grid_options->draw(dc);
+ }
+
+private:
+ void selectOption(TextView* opt)
+ {
+ if (selected_option != nullptr)
+ {
+ selected_option->setBackgroundColor(col_bg_normal);
+ }
+
+ selected_option = opt;
+
+ selected_option->setBackgroundColor(col_bg_highlight);
+ }
+
+ void onOptionClick(View* option, Interaction action)
+ {
+ if (action == Interaction::CLICK)
+ {
+ TextView* textview = static_cast<TextView*>(option);
+
+ selectOption(textview);
+
+ for (auto l : opt_listeners)
+ {
+ if (l != nullptr)
+ {
+ l(map_options[textview]);
+ }
+ }
+ }
+ }
+
+ TextView* tv_title;
+ mxgui::Color col_bg_normal = mxgui::black;
+ mxgui::Color col_bg_highlight = mxgui::blue;
+
+ GridLayout* grid_options;
+ std::map<TextView*, uint8_t> map_options;
+
+ std::vector<OnOptionChosenListener> opt_listeners;
+
+ TextView* selected_option = nullptr;
+};
\ No newline at end of file
diff --git a/src/shared/utils/gui/ScreenManager.h b/src/shared/utils/gui/ScreenManager.h
new file mode 100644
index 000000000..b8b34e352
--- /dev/null
+++ b/src/shared/utils/gui/ScreenManager.h
@@ -0,0 +1,125 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <miosix.h>
+
+#include <map>
+#include <deque>
+
+#include "NavController.h"
+#include "View.h"
+
+/**
+ * @brief UI Thread: Manages multiple view trees ("Screen") and draws the active
+ * one at the provided refresh rate
+ */
+class ScreenManager : public ActiveObject
+{
+public:
+ ScreenManager(mxgui::DisplayManager& display, unsigned int refresh_rate)
+ : dc(display.getDisplay()), refresh_interval(1000 / refresh_rate)
+ {
+ }
+
+ void showScreen(uint8_t id)
+ {
+ active_screen = id;
+ controller.updateViewTree(screens[id]);
+ }
+
+ uint8_t getScreen() { return active_screen; }
+
+ void addScreen(uint8_t id, View* root)
+ {
+ screens[id] = root;
+
+ root->setBounds({{0, 0}, {dc.getWidth(), dc.getHeight()}});
+
+ if (screens.size() == 1)
+ {
+ showScreen(id);
+ }
+ }
+
+ void onButtonPress(ButtonPress press) { controller.onButtonPress(press); }
+
+ mxgui::DrawingContext& getDrawingContext() { return dc; }
+
+protected:
+ void run() override
+ {
+ uint8_t last_screen = 0;
+ while (!shouldStop())
+ {
+ if (active_screen != last_screen)
+ {
+ last_screen = active_screen;
+ dc.clear(mxgui::black);
+ screens[active_screen]->invalidateTree();
+ }
+
+ long long start = miosix::getTick();
+
+ drawViewTree(screens[active_screen], dc);
+
+ Thread::sleepUntil(start + refresh_interval);
+ }
+ }
+private:
+ /**
+ * @brief Draws the provided view tree on screen
+ *
+ * @param root Root of the view tree
+ * @param dc Drawing context
+ */
+ void drawViewTree(View* root, mxgui::DrawingContext& dc)
+ {
+ // Avoid recursion
+ std::deque<View*> views_dc;
+ views_dc.push_back(root);
+
+ while(views_dc.size() != 0)
+ {
+ View* view = views_dc.front();
+ views_dc.pop_front();
+
+ view->draw(dc);
+
+ for(View* c : view->getChilds())
+ {
+ views_dc.push_back(c);
+ }
+ }
+ }
+
+ mxgui::DrawingContext dc;
+
+ unsigned int refresh_interval;
+
+ NavController controller;
+ std::map<uint8_t, View*> screens;
+
+ uint8_t active_screen = 0;
+};
\ No newline at end of file
diff --git a/src/shared/utils/gui/TextView.h b/src/shared/utils/gui/TextView.h
new file mode 100644
index 000000000..a695e0ccd
--- /dev/null
+++ b/src/shared/utils/gui/TextView.h
@@ -0,0 +1,181 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <mxgui/misc_inst.h>
+
+#include "View.h"
+
+/**
+ * @brief Simple view to display text on screen.
+ *
+ */
+class TextView : public View
+{
+public:
+ /**
+ * @brief Creates a new TextView
+ *
+ * @param text Text
+ * @param text_color Text color
+ * @param padding Spacing from view bounds in pixels
+ */
+ TextView(std::string text = "", mxgui::Color text_color = mxgui::white,
+ uint8_t padding = 1)
+ : View(), text(text), col_text(text_color), padding(padding)
+ {
+ }
+
+ void setVerticalAlignment(VertAlignment alignment)
+ {
+ vert_align = alignment;
+ invalidate();
+ }
+
+ void setHorizontalAlignment(HorizAlignment alignment)
+ {
+ horiz_align = alignment;
+ invalidate();
+ }
+
+ void setAlignment(HorizAlignment horiz_align, VertAlignment vert_align)
+ {
+ setVerticalAlignment(vert_align);
+ setHorizontalAlignment(horiz_align);
+ }
+
+ void setText(std::string text)
+ {
+ if (text != this->text)
+ {
+ invalidate();
+ this->text = text;
+ }
+ }
+
+ std::string getText() { return text; }
+
+ virtual void setTextColor(mxgui::Color color)
+ {
+ if (color != col_text)
+ {
+ this->col_text = color;
+ invalidate();
+ }
+ }
+
+ mxgui::Color getTextColor() { return col_text; }
+
+ virtual void draw(mxgui::DrawingContext& dc) override
+ {
+ if (isInvalidated())
+ {
+ View::draw(dc);
+
+ Bounds padded_bounds = getBounds();
+ padded_bounds.pos += Position{padding, padding};
+ padded_bounds.size.height -= padding;
+ padded_bounds.size.width -= padding;
+
+ Position top_left = padded_bounds.topLeft();
+ switch (vert_align)
+ {
+ case VertAlignment::BOTTOM:
+ top_left.y =
+ padded_bounds.bottomLeft().y - dc.getFont().getHeight();
+ break;
+ case VertAlignment::CENTER:
+ top_left.y =
+ padded_bounds.pos.y +
+ (padded_bounds.size.height - dc.getFont().getHeight()) /
+ 2;
+ break;
+ case VertAlignment::TOP:
+ break;
+ }
+
+ switch (horiz_align)
+ {
+ case HorizAlignment::RIGHT:
+ top_left.x =
+ padded_bounds.topRight().x - getTextWidth(text);
+ break;
+ case HorizAlignment::CENTER:
+ top_left.x =
+ padded_bounds.pos.x +
+ (padded_bounds.size.width - getTextWidth(text)) / 2;
+ break;
+ case HorizAlignment::LEFT:
+ break;
+ }
+
+ mxgui::Font old_font = dc.getFont();
+ dc.setFont(font);
+
+ dc.setTextColor(getTextColor(), getBackgroundColor());
+ dc.clippedWrite(top_left, padded_bounds.topLeft(),
+ padded_bounds.bottomRight(), text);
+
+ dc.setFont(old_font);
+ }
+ }
+
+ void setFont(mxgui::Font font)
+ {
+ this->font = font;
+ invalidate();
+ }
+
+ mxgui::Font getFont() { return font; }
+
+protected:
+ short int getTextWidth(std::string text)
+ {
+ short int w = 0;
+ for (char c : text)
+ {
+ if (c >= font.getStartChar() && c <= font.getEndChar())
+ {
+ if (font.isFixedWidth())
+ {
+ w += font.getWidth();
+ }
+ else
+ {
+ w += font.getWidths()[c - font.getStartChar()];
+ }
+ }
+ }
+ return w;
+ }
+
+private:
+ mxgui::Font font = mxgui::tahoma;
+ VertAlignment vert_align = VertAlignment::TOP;
+ HorizAlignment horiz_align = HorizAlignment::LEFT;
+ std::string text;
+
+ mxgui::Color col_text = mxgui::white;
+ uint8_t padding;
+};
\ No newline at end of file
diff --git a/src/shared/utils/gui/VerticalLayout.h b/src/shared/utils/gui/VerticalLayout.h
new file mode 100644
index 000000000..7d19c2eb0
--- /dev/null
+++ b/src/shared/utils/gui/VerticalLayout.h
@@ -0,0 +1,124 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "View.h"
+
+/**
+ * @brief Positions the childs in a vertical grid. The height of each child is
+ * dictated by its weight parameter
+ */
+class VerticalLayout : public View
+{
+public:
+ /**
+ * @brief Creates a new VerticalLayout
+ *
+ * @param spacing Vertical space between each child in pixels
+ */
+ VerticalLayout(short int spacing = 0) : View(), spacing(spacing) {}
+
+ /**
+ * @brief Adds a view to the layout.
+ *
+ * @param view Pointer to the view to be added
+ * @param weight Relative weight of the view to determine its display height
+ */
+ void addView(View* view, float weight)
+ {
+ if (weight < 0)
+ weight = 0;
+ childs.push_back({weight, view});
+ weigth_sum += weight;
+
+ // Recalculated child bounds
+ updateChildBounds();
+
+ // Redraw
+ invalidate();
+ }
+
+ void setBounds(Bounds bounds) override
+ {
+ View::setBounds(bounds);
+
+ updateChildBounds();
+ }
+
+ void draw(mxgui::DrawingContext& dc) override
+ {
+ View::draw(dc);
+
+ for (auto view : childs)
+ {
+ view.drawable->draw(dc);
+ }
+ }
+
+ virtual std::vector<View*> getChilds() override
+ {
+ std::vector<View*> out;
+ out.reserve(childs.size());
+ for (auto v : childs)
+ {
+ out.push_back(v.drawable);
+ }
+ return out;
+ }
+
+private:
+ void updateChildBounds()
+ {
+ if (weigth_sum > 0)
+ {
+ short int aval_height =
+ getBounds().size.height - (childs.size() - 1) * spacing;
+
+ short int y = 0;
+
+ for (auto view : childs)
+ {
+ Bounds view_bounds{{0, y}, {getBounds().size.width, 0}};
+
+ view_bounds.size.height =
+ aval_height / weigth_sum * view.vert_weight;
+ view_bounds.pos += getBounds().pos;
+
+ view.drawable->setBounds(view_bounds);
+
+ y += view_bounds.size.height + spacing;
+ }
+ }
+ }
+
+ struct Child
+ {
+ float vert_weight;
+ View* drawable;
+ };
+
+ float weigth_sum = 0;
+ short int spacing;
+ std::vector<Child> childs;
+};
\ No newline at end of file
diff --git a/src/shared/utils/gui/View.h b/src/shared/utils/gui/View.h
new file mode 100644
index 000000000..ffa4a8449
--- /dev/null
+++ b/src/shared/utils/gui/View.h
@@ -0,0 +1,261 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <mxgui/display.h>
+#include <mxgui/misc_inst.h>
+
+#include <functional>
+
+#include "Misc.h"
+
+struct Size
+{
+ short int width;
+ short int height;
+};
+
+struct Position
+{
+ short int x;
+ short int y;
+
+ operator mxgui::Point() { return mxgui::Point(x, y); }
+
+ Position operator+(const Position& other)
+ {
+ return {short(x + other.x), short(y + other.y)};
+ }
+
+ Position operator-(const Position& other)
+ {
+ return {short(x - other.x), short(y - other.y)};
+ }
+
+ void operator+=(const Position& other)
+ {
+ x += other.x;
+ y += other.y;
+ }
+
+ void operator-=(const Position& other)
+ {
+ x -= other.x;
+ y -= other.y;
+ }
+
+ void print() { printf("pos: %d %d\n", x, y); }
+};
+
+struct Bounds
+{
+ Position pos;
+ Size size;
+
+ Position topLeft() { return {pos.x, pos.y}; }
+ Position topRight() { return {short(pos.x + size.width - 1), pos.y}; }
+
+ Position bottomRight()
+ {
+ return {short(pos.x + size.width - 1), short(pos.y + size.height - 1)};
+ }
+
+ Position bottomLeft()
+ {
+ return {pos.x, short(pos.y + short(size.height - 1))};
+ }
+
+ void print()
+ {
+ printf("Bounds: p:{%d %d} s:{%d %d}\n", pos.x, pos.y, size.width,
+ size.height);
+ }
+};
+
+enum class VertAlignment
+{
+ TOP,
+ CENTER,
+ BOTTOM
+};
+
+enum class HorizAlignment
+{
+ LEFT,
+ CENTER,
+ RIGHT
+};
+
+enum class Interaction
+{
+ BTN_DOWN,
+ BTN_UP,
+ CLICK,
+ LONG_CLICK
+};
+
+/**
+ * @brief Base class for anything that can be drawn on the screen and interacted
+ * with
+ */
+class View
+{
+public:
+ using OnInteractionListener = std::function<void(View*, Interaction)>;
+
+ View() {}
+ virtual ~View() {}
+
+ /**
+ * @brief Sets the bounds in which the view will be drawn
+ */
+ virtual void setBounds(Bounds bounds)
+ {
+ this->bounds = bounds;
+ invalidate();
+ }
+
+ Bounds getBounds() { return bounds; }
+
+ /**
+ * @brief Signal that what has been previously drawn is now invalid and has
+ * to be redrawn.
+ */
+ void invalidate()
+ {
+ if (!invalidated)
+ {
+ invalidated = true;
+ }
+ }
+
+ /**
+ * @brief Invalidate the view tree which has this view as a root
+ */
+ void invalidateTree()
+ {
+ invalidate();
+
+ std::vector<View*> childs = getChilds();
+ for (auto child : childs)
+ {
+ child->invalidateTree();
+ }
+ }
+
+ /**
+ * @brief Draw the view in its bounds
+ *
+ * @param dc Reference to a drawingcontext
+ */
+ virtual void draw(mxgui::DrawingContext& dc)
+ {
+ if (invalidated)
+ {
+ invalidated = false;
+
+ dc.clear(bounds.topLeft(), bounds.bottomRight(),
+ getBackgroundColor());
+
+ dc.drawRectangle(bounds.topLeft(), bounds.bottomRight(),
+ selected ? col_selected : getBackgroundColor());
+ }
+ }
+
+ /**
+ * @brief Returns the list of childs
+ *
+ * @return std::vector<View*>
+ */
+ virtual std::vector<View*> getChilds()
+ {
+ // Default behavious is no childs
+ return {};
+ }
+
+ virtual void setBackgroundColor(mxgui::Color color)
+ {
+ if (color != col_bg)
+ {
+ col_bg = color;
+ invalidate();
+ }
+ }
+
+ virtual mxgui::Color getBackgroundColor() { return col_bg; }
+
+ void setSelectedColor(mxgui::Color color)
+ {
+ col_selected = color;
+ invalidate();
+ }
+
+ void setSelectable(bool selectable) { this->selectable = selectable; }
+
+ bool isSelectable() { return selectable; }
+
+ void setSelected(bool selected)
+ {
+ this->selected = selected;
+ invalidate();
+ }
+
+ bool isSelected() { return selected && selectable; }
+
+ void addOnInteractionListener(OnInteractionListener listener)
+ {
+ listeners.push_back(listener);
+ }
+
+ virtual void performInteraction(Interaction action)
+ {
+ for (auto lst : listeners)
+ {
+ if (lst)
+ {
+ lst(this, action);
+ }
+ }
+ }
+
+protected:
+ bool isInvalidated() { return invalidated; }
+
+private:
+ mxgui::Color col_selected = mxgui::red;
+ mxgui::Color col_bg = mxgui::black;
+
+ std::vector<OnInteractionListener> listeners;
+
+ bool selected = false;
+ bool selectable = false;
+ bool invalidated = true;
+
+ Bounds bounds{{0, 0}, {0, 0}};
+
+ // Drawing big solid chunks (such as backgrounds) takes a lot of time: do
+ // not redraw if not necessary
+ bool was_selected = false;
+ mxgui::Color last_bg_color = col_bg;
+};
diff --git a/src/shared/utils/testutils/MockGpioPin.h b/src/shared/utils/testutils/MockGpioPin.h
new file mode 100644
index 000000000..d3246d4d8
--- /dev/null
+++ b/src/shared/utils/testutils/MockGpioPin.h
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2021 Luca Erbetta
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <miosix.h>
+
+#include <memory>
+
+class MockGpioPin
+{
+public:
+ MockGpioPin()
+ : val(new int(0)),
+ gpio_mode(new miosix::Mode::Mode_(miosix::Mode::OUTPUT))
+ {
+ }
+
+ void mode(miosix::Mode::Mode_ m) { *gpio_mode = m; }
+
+ void high() { *val = 1; }
+
+ void low() { *val = 0; }
+
+ int value() { return *val; }
+
+ // shared_ptr: make all copies of this instance share the same val /
+ // gpio_mode
+ std::shared_ptr<int> val;
+ std::shared_ptr<miosix::Mode::Mode_> gpio_mode;
+};
\ No newline at end of file
diff --git a/src/shared/utils/testutils/ThroughputCalculator.h b/src/shared/utils/testutils/ThroughputCalculator.h
new file mode 100644
index 000000000..49b59cfe5
--- /dev/null
+++ b/src/shared/utils/testutils/ThroughputCalculator.h
@@ -0,0 +1,176 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <miosix.h>
+
+#include <deque>
+#include <limits>
+
+using miosix::FastMutex;
+using miosix::Lock;
+using std::deque;
+
+struct DataRateResult
+{
+ float data_rate;
+ float packet_loss;
+ float packets_per_second;
+};
+
+/**
+ * Helper class for calculating the throughput of communications. Just
+ * pass the size of a packet to addPacket(...) as soon as the packet is
+ * received.
+ */
+class ThroughputCalculator
+{
+public:
+ /**
+ * @brief Creates a new ThroughputCalculator
+ *
+ * @param expected_pkt_interval Expected packet delivery interval for packet
+ * loss estimation
+ * @param window_duration Duration of the observation window. Longer windows
+ * means more stable values but slow response to abrupt changes
+ */
+ ThroughputCalculator(unsigned int expected_pkt_interval,
+ unsigned int window_duration = 2000)
+ : expected_pkt_interval(expected_pkt_interval),
+ window_duration(window_duration)
+ {
+ }
+
+ /**
+ * @brief Signal that a new packet has arrived. Call this as soon as the
+ * packet is available
+ *
+ * @param packet_size The size of the packet
+ */
+ void addPacket(size_t packet_size)
+ {
+ Lock<FastMutex> lock(mutex_pkt);
+
+ long long ts = miosix::getTick();
+
+ unsigned int interval = 0;
+ if (packets.size() > 0)
+ {
+ interval = static_cast<unsigned int>(ts - packets.back().timestamp);
+ }
+
+ packets.push_back({ts, packet_size, interval});
+
+ removeOldPackets(ts);
+ }
+
+ /**
+ * @brief Returns the datarate in Bytes/second averaged over the duration of
+ * the window
+ */
+ float getDataRate()
+ {
+ Lock<FastMutex> lock(mutex_pkt);
+
+ long long ts = miosix::getTick();
+ removeOldPackets(ts);
+
+ float sum = 0;
+ for (size_t i = 0; i < packets.size(); i++)
+ {
+ sum += packets[i].size;
+ }
+ return sum / (window_duration / 1000.0f);
+ }
+
+ /**
+ * @brief Returns the packet loss percentage ([0-1]) based on the expected packet
+ * delivery interval
+ */
+ float getPacketLoss()
+ {
+ Lock<FastMutex> lock(mutex_pkt);
+ long long ts = miosix::getTick();
+ removeOldPackets(ts);
+
+ float avg_interval = std::numeric_limits<float>::infinity();
+ if (packets.size() > 0)
+ {
+ avg_interval = packets[0].interval;
+ for (size_t i = 1; i < packets.size(); i++)
+ {
+ avg_interval += packets[i].interval;
+ }
+
+ avg_interval /= packets.size();
+ }
+
+ return 1 - expected_pkt_interval / avg_interval;
+ }
+
+ /**
+ * @brief Returns the pps value averaged over the duration of the window
+ */
+ float getPacketsPerSecond()
+ {
+ Lock<FastMutex> lock(mutex_pkt);
+ long long ts = miosix::getTick();
+ removeOldPackets(ts);
+
+ return (float)packets.size() / (window_duration / 1000.0f);
+ }
+
+ DataRateResult getResult()
+ {
+ DataRateResult r;
+ r.data_rate = getDataRate();
+ r.packet_loss = getPacketLoss();
+ r.packets_per_second = getPacketsPerSecond();
+
+ return r;
+ }
+
+private:
+ struct Packet
+ {
+ long long timestamp;
+ size_t size;
+ unsigned int interval;
+ };
+
+ void removeOldPackets(long long ts)
+ {
+ while (packets.size() > 0 &&
+ packets.front().timestamp < ts - window_duration)
+ {
+ packets.pop_front();
+ }
+ }
+
+ unsigned int expected_pkt_interval;
+ unsigned int window_duration;
+ deque<Packet> packets;
+
+ FastMutex mutex_pkt;
+};
\ No newline at end of file
diff --git a/src/tests/catch/spidriver/FakeSPIBus.h b/src/tests/catch/spidriver/FakeSPIBus.h
deleted file mode 100644
index d1213bfd6..000000000
--- a/src/tests/catch/spidriver/FakeSPIBus.h
+++ /dev/null
@@ -1,266 +0,0 @@
-/**
- * Copyright (c) 2020 Skyward Experimental Rocketry
- * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
- *
- * 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.
- */
-
-#include "FakeSpiTypedef.h"
-#include "drivers/spi/SPIBusInterface.h"
-
-#pragma once
-
-/**
- * @brief SPIBus modified to accept a fake Chip select & spi peripheral struct.
- * Not ideal as modifications to SPIBus have to be manually applied here too,
- * but that's the only way I found to test it effectively
- */
-class FakeSPIBus : public SPIBusInterface
-{
-public:
- /**
- * @brief Instantiates a new FakeSPIBus
- *
- * @param spi Pointer to the SPI peripheral to be used
- */
- FakeSPIBus(FakeSpiTypedef* spi);
- ~FakeSPIBus() {}
-
- // Delete copy/move contructors/operators
- FakeSPIBus(const FakeSPIBus&) = delete;
- FakeSPIBus& operator=(const FakeSPIBus&) = delete;
-
- FakeSPIBus(FakeSPIBus&&) = delete;
- FakeSPIBus& operator=(FakeSPIBus&&) = delete;
-
- void disableBusConfiguration() { config_enabled = false; }
-
- /**
- * @brief See SPIBusInterface::write()
- */
- void write(uint8_t byte) override;
-
- /**
- * @brief See SPIBusInterface::write()
- */
- void write(uint8_t* data, size_t size) override;
-
- /**
- * @brief See SPIBusInterface::read()
- */
- uint8_t read() override;
-
- /**
- * @brief See SPIBusInterface::read()
- */
- void read(uint8_t* data, size_t size) override;
-
- /**
- * @brief See SPIBusInterface::transfer()
- */
- uint8_t transfer(uint8_t data) override;
-
- /**
- * @brief See SPIBusInterface::transfer()
- */
- void transfer(uint8_t* data, size_t size) override;
-
- /**
- * @brief See SPIBusInterface::select()
- */
- void select(GpioPin& cs) override;
-
- /**
- * @brief See SPIBusInterface::deselect()
- */
- void deselect(GpioPin& cs) override;
-
- /**
- * @brief See SPIBusInterface::configure()
- */
- void configure(SPIBusConfig config) override;
-
-private:
- /**
- * Writes a single byte on the SPI bus.
- *
- * @param byte Pointer to the byte to be written
- */
- void write(uint8_t* byte);
-
- /**
- * Reads a single byte from the SPI bus.
- *
- * @param byte Pointer to the byte where the read data will be stored
- */
- void read(uint8_t* byte);
-
- /**
- * Full duplex transfer. Writes a single byte on the SPI bus and replaces
- * its content with the received data
- *
- * @param byte Pointer to the byte to be transfered
- */
- void transfer(uint8_t* byte);
-
- FakeSpiTypedef* spi;
-
- SPIBusConfig config;
- bool config_enabled = true;
- bool first_config_applied = false;
-};
-
-FakeSPIBus::FakeSPIBus(FakeSpiTypedef* spi) : spi(spi) {}
-
-void FakeSPIBus::configure(SPIBusConfig new_config)
-{
- // Reconfigure the bus only if config enabled. Do not reconfigure if already
- // in the correct configuration.
- if (config_enabled && (!first_config_applied || new_config != config))
- {
- first_config_applied = true;
- config = new_config;
-
- // Clean CR1
- spi->CR1 = 0;
-
- // Configure CPOL & CPHA bits
- spi->CR1 |= static_cast<uint32_t>(config.mode);
-
- // Configure clock division (BR bits)
- spi->CR1 |= static_cast<uint32_t>(config.clock_div);
-
- // Configure LSBFIRST bit
- spi->CR1 |= static_cast<uint32_t>(config.bit_order);
-
- spi->CR1 |= SPI_CR1_SSI | SPI_CR1_SSM // Use software chip-select
- | SPI_CR1_MSTR // Master mode
- | SPI_CR1_SPE; // Enable SPI
- }
-}
-
-inline void FakeSPIBus::write(uint8_t data) { write(&data); }
-
-inline void FakeSPIBus::write(uint8_t* data, size_t size)
-{
- for (size_t i = 0; i < size; i++)
- {
- write(data + i);
- }
-}
-
-inline uint8_t FakeSPIBus::read()
-{
- uint8_t data;
- read(&data);
-
- return data;
-}
-
-inline void FakeSPIBus::read(uint8_t* data, size_t size)
-{
- for (size_t i = 0; i < size; i++)
- {
- read(data + i);
- }
-}
-
-inline uint8_t FakeSPIBus::transfer(uint8_t data)
-{
- transfer(&data);
- return data;
-}
-
-inline void FakeSPIBus::transfer(uint8_t* data, size_t size)
-{
- for (size_t i = 0; i < size; i++)
- {
- transfer(data + i);
- }
-}
-
-inline void FakeSPIBus::select(GpioPin& rcs)
-{
- FakeGpioPin& cs = static_cast<FakeGpioPin&>(rcs);
-
- cs.low();
- if (config.cs_setup_time_us > 0)
- {
- delayUs(config.cs_setup_time_us);
- }
-}
-
-inline void FakeSPIBus::deselect(GpioPin& rcs)
-{
- FakeGpioPin& cs = static_cast<FakeGpioPin&>(rcs);
-
- if (config.cs_hold_time_us > 0)
- {
- delayUs(config.cs_hold_time_us);
- }
- cs.high();
-}
-
-inline void FakeSPIBus::write(uint8_t* byte)
-{
- // Wait until the peripheral is ready to transmit
- while ((spi->SR & SPI_SR_TXE) == 0)
- ;
- // Write the byte in the transmit buffer
- spi->DR = *byte;
-
- // Wait until byte is transmitted
- while ((spi->SR & SPI_SR_RXNE) == 0)
- ;
-
- // Clear the RX buffer by accessing the DR register
- (void)spi->DR;
-}
-
-inline void FakeSPIBus::transfer(uint8_t* byte)
-{
- // Wait until the peripheral is ready to transmit
- while ((spi->SR & SPI_SR_TXE) == 0)
- ;
- // Write the byte in the transmit buffer
- spi->DR = *byte;
-
- // Wait until byte is transmitted
- while ((spi->SR & SPI_SR_RXNE) == 0)
- ;
-
- // Store the received data in the byte
- *byte = (uint8_t)spi->DR;
-}
-
-inline void FakeSPIBus::read(uint8_t* byte)
-{
- // Wait until the peripheral is ready to transmit
- while ((spi->SR & SPI_SR_TXE) == 0)
- ;
- // Write the byte in the transmit buffer
- spi->DR = 0;
-
- // Wait until byte is transmitted
- while ((spi->SR & SPI_SR_RXNE) == 0)
- ;
-
- // Store the received data in the byte
- *byte = (uint8_t)spi->DR;
-}
\ No newline at end of file
diff --git a/src/tests/catch/spidriver/test-spidriver.cpp b/src/tests/catch/spidriver/test-spidriver.cpp
index e0facfd1b..26773eed2 100644
--- a/src/tests/catch/spidriver/test-spidriver.cpp
+++ b/src/tests/catch/spidriver/test-spidriver.cpp
@@ -25,11 +25,16 @@
#include "../catch-tests-entry.cpp"
#endif
+#ifndef USE_MOCK_PERIPHERALS
+#error "This test requires SpiBusInterface built with MockGpioPin (-DUSE_MOCK_PERIPHERALS)"
+#endif
+
#include <utils/testutils/catch.hpp>
-#include "FakeSPIBus.h"
-#include "drivers/spi/MockSPIBus.h"
+#include "drivers/spi/SPIBus.h"
#include "drivers/spi/SPIDriver.h"
+#include "drivers/spi/test/FakeSpiTypedef.h"
+#include "drivers/spi/test/MockSPIBus.h"
template <typename T1, typename T2>
bool bufcmp(T1* buf1, T2* buf2, size_t size)
@@ -49,7 +54,7 @@ TEST_CASE("SPIBus - Bus Configuration")
{
FakeSpiTypedef spi;
- FakeSPIBus bus{&spi};
+ SPIBus bus{&spi};
REQUIRE(spi.CR1 == 0);
@@ -62,79 +67,93 @@ TEST_CASE("SPIBus - Bus Configuration")
{
config.mode = SPIMode::MODE0;
uint32_t expected_CR1 = 0x0344;
- bus.configure(config);
+ bus.acquire(config);
REQUIRE(spi.CR1 == expected_CR1);
+ bus.release();
config.mode = SPIMode::MODE1;
expected_CR1 = 0x0345;
- bus.configure(config);
+ bus.acquire(config);
REQUIRE(spi.CR1 == expected_CR1);
+ bus.release();
config.mode = SPIMode::MODE2;
expected_CR1 = 0x0346;
- bus.configure(config);
+ bus.acquire(config);
REQUIRE(spi.CR1 == expected_CR1);
+ bus.release();
config.mode = SPIMode::MODE3;
expected_CR1 = 0x0347;
- bus.configure(config);
+ bus.acquire(config);
REQUIRE(spi.CR1 == expected_CR1);
+ bus.release();
}
SECTION("Clock Divider")
{
config.clock_div = SPIClockDivider::DIV2;
uint32_t expected_CR1 = 0x0344;
- bus.configure(config);
+ bus.acquire(config);
REQUIRE(spi.CR1 == expected_CR1);
+ bus.release();
config.clock_div = SPIClockDivider::DIV4;
expected_CR1 = 0x034C;
- bus.configure(config);
+ bus.acquire(config);
REQUIRE(spi.CR1 == expected_CR1);
+ bus.release();
config.clock_div = SPIClockDivider::DIV8;
expected_CR1 = 0x0354;
- bus.configure(config);
+ bus.acquire(config);
REQUIRE(spi.CR1 == expected_CR1);
+ bus.release();
config.clock_div = SPIClockDivider::DIV16;
expected_CR1 = 0x035C;
- bus.configure(config);
+ bus.acquire(config);
REQUIRE(spi.CR1 == expected_CR1);
+ bus.release();
config.clock_div = SPIClockDivider::DIV32;
expected_CR1 = 0x0364;
- bus.configure(config);
+ bus.acquire(config);
REQUIRE(spi.CR1 == expected_CR1);
+ bus.release();
config.clock_div = SPIClockDivider::DIV64;
expected_CR1 = 0x036C;
- bus.configure(config);
+ bus.acquire(config);
REQUIRE(spi.CR1 == expected_CR1);
+ bus.release();
config.clock_div = SPIClockDivider::DIV128;
expected_CR1 = 0x0374;
- bus.configure(config);
+ bus.acquire(config);
REQUIRE(spi.CR1 == expected_CR1);
+ bus.release();
config.clock_div = SPIClockDivider::DIV256;
expected_CR1 = 0x037C;
- bus.configure(config);
+ bus.acquire(config);
REQUIRE(spi.CR1 == expected_CR1);
+ bus.release();
}
SECTION("Bit order")
{
config.bit_order = SPIBitOrder::MSB_FIRST;
uint32_t expected_CR1 = 0x0344;
- bus.configure(config);
+ bus.acquire(config);
REQUIRE(spi.CR1 == expected_CR1);
+ bus.release();
config.bit_order = SPIBitOrder::LSB_FIRST;
expected_CR1 = 0x03C4;
- bus.configure(config);
+ bus.acquire(config);
REQUIRE(spi.CR1 == expected_CR1);
+ bus.release();
}
}
@@ -147,8 +166,9 @@ TEST_CASE("SPIBus - Bus Configuration")
config.bit_order = SPIBitOrder::LSB_FIRST;
bus.disableBusConfiguration();
- bus.configure(config);
+ bus.acquire(config);
REQUIRE(spi.CR1 == 0);
+ bus.release();
}
}
@@ -156,7 +176,7 @@ TEST_CASE("SPIBus - Chip select")
{
FakeSpiTypedef spi;
- FakeSPIBus bus{&spi};
+ SPIBus bus{&spi};
REQUIRE(spi.cs.value() == 1);
@@ -174,7 +194,7 @@ TEST_CASE("SPIBus - One byte operations")
spi.DR.in_buf = {1, 2, 3, 4, 5, 6, 7, 8, 9};
spi.CR1_expected = 0x03DF;
- FakeSPIBus bus{&spi};
+ SPIBus bus{&spi};
SPIBusConfig config;
config.clock_div = SPIClockDivider::DIV16;
@@ -182,7 +202,7 @@ TEST_CASE("SPIBus - One byte operations")
config.mode = SPIMode::MODE3;
config.bit_order = SPIBitOrder::LSB_FIRST;
- bus.configure(config);
+ bus.acquire(config);
bus.select(spi.cs);
SECTION("Write")
@@ -216,6 +236,9 @@ TEST_CASE("SPIBus - One byte operations")
REQUIRE(spi.DR.out_buf.back() == 255);
REQUIRE(spi.DR.out_buf.size() == 2);
}
+
+ bus.deselect(spi.cs);
+ bus.release();
}
TEST_CASE("SPIBus - Multi byte operations")
@@ -225,7 +248,7 @@ TEST_CASE("SPIBus - Multi byte operations")
spi.DR.in_buf = {100, 101, 102, 103, 104, 105, 106, 107, 108};
spi.CR1_expected = 0x03DF;
- FakeSPIBus bus{&spi};
+ SPIBus bus{&spi};
SPIBusConfig config;
config.clock_div = SPIClockDivider::DIV16;
@@ -233,7 +256,7 @@ TEST_CASE("SPIBus - Multi byte operations")
config.mode = SPIMode::MODE3;
config.bit_order = SPIBitOrder::LSB_FIRST;
- bus.configure(config);
+ bus.acquire(config);
bus.select(spi.cs);
// 2 identical buffers
@@ -293,30 +316,33 @@ TEST_CASE("SPIBus - Multi byte operations")
// No overflows
REQUIRE(bufcmp(bufc + 4, buf + 4, 1));
}
+
+ bus.deselect(spi.cs);
+ bus.release();
}
TEST_CASE("SPITransaction - writes")
{
- MockSPIBus bus{};
SPIBusConfig config1{};
config1.mode = SPIMode::MODE1;
config1.clock_div = SPIClockDivider::DIV32;
- bus.expected_config = config1;
+ MockSPIBus bus(config1);
+ MockGpioPin cs;
SECTION("Transaction")
{
- SPITransaction spi(bus, GpioPin(GPIOA_BASE, 1), config1);
+ SPITransaction spi(bus, cs, config1);
- REQUIRE(bus.out_buf.size() == 0);
+ REQUIRE(bus.getOutBuf().size() == 0);
SECTION("cmd write")
{
spi.write(9);
REQUIRE_FALSE(bus.isSelected());
- REQUIRE(bus.out_buf.size() == 1);
- REQUIRE(bus.out_buf.back() == 9);
+ REQUIRE(bus.getOutBuf().size() == 1);
+ REQUIRE(bus.getOutBuf().back() == 9);
}
SECTION("1 byte reg write")
@@ -324,9 +350,9 @@ TEST_CASE("SPITransaction - writes")
spi.write(10, 77);
REQUIRE_FALSE(bus.isSelected());
- REQUIRE(bus.out_buf.size() == 2);
- REQUIRE(bus.out_buf[0] == 10);
- REQUIRE(bus.out_buf[1] == 77);
+ REQUIRE(bus.getOutBuf().size() == 2);
+ REQUIRE(bus.getOutBuf()[0] == 10);
+ REQUIRE(bus.getOutBuf()[1] == 77);
}
SECTION("multi byte reg write")
@@ -338,8 +364,8 @@ TEST_CASE("SPITransaction - writes")
spi.write(10, buf, 0);
REQUIRE_FALSE(bus.isSelected());
- REQUIRE(bus.out_buf.size() == 1);
- REQUIRE(bus.out_buf[0] == 10);
+ REQUIRE(bus.getOutBuf().size() == 1);
+ REQUIRE(bus.getOutBuf()[0] == 10);
}
SECTION("2 writes")
@@ -347,18 +373,18 @@ TEST_CASE("SPITransaction - writes")
spi.write(10, buf, 4);
REQUIRE_FALSE(bus.isSelected());
- REQUIRE(bus.out_buf.size() == 5);
+ REQUIRE(bus.getOutBuf().size() == 5);
- REQUIRE(bus.out_buf[0] == 10);
- REQUIRE(bufcmp(buf, bus.out_buf.data() + 1, 4));
+ REQUIRE(bus.getOutBuf()[0] == 10);
+ REQUIRE(bufcmp(buf, bus.getOutBuf().data() + 1, 4));
spi.write(99, buf, 6);
REQUIRE_FALSE(bus.isSelected());
- REQUIRE(bus.out_buf.size() == 12);
+ REQUIRE(bus.getOutBuf().size() == 12);
- REQUIRE(bus.out_buf[5] == 99);
- REQUIRE(bufcmp(buf, bus.out_buf.data() + 6, 6));
+ REQUIRE(bus.getOutBuf()[5] == 99);
+ REQUIRE(bufcmp(buf, bus.getOutBuf().data() + 6, 6));
}
}
@@ -369,65 +395,65 @@ TEST_CASE("SPITransaction - writes")
spi.write(buf, 0);
REQUIRE_FALSE(bus.isSelected());
- REQUIRE(bus.out_buf.size() == 0);
+ REQUIRE(bus.getOutBuf().size() == 0);
spi.write(buf, 4);
REQUIRE_FALSE(bus.isSelected());
- REQUIRE(bus.out_buf.size() == 4);
+ REQUIRE(bus.getOutBuf().size() == 4);
- REQUIRE(bufcmp(buf, bus.out_buf.data(), 4));
+ REQUIRE(bufcmp(buf, bus.getOutBuf().data(), 4));
spi.write(buf, 6);
REQUIRE_FALSE(bus.isSelected());
- REQUIRE(bus.out_buf.size() == 10);
+ REQUIRE(bus.getOutBuf().size() == 10);
- REQUIRE(bufcmp(buf, bus.out_buf.data() + 4, 6));
+ REQUIRE(bufcmp(buf, bus.getOutBuf().data() + 4, 6));
}
}
}
TEST_CASE("SPITransaction - reads")
{
- MockSPIBus bus;
-
- bus.in_buf = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109};
-
SPIBusConfig config1;
config1.mode = SPIMode::MODE1;
config1.clock_div = SPIClockDivider::DIV32;
- bus.expected_config = config1;
+ MockSPIBus bus(config1);
+ MockGpioPin cs;
+
+ uint8_t in_data[10] = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109};
+ bus.push(in_data, 10);
SECTION("Transaction")
{
- SPISlave slave(bus, GpioPin(GPIOA_BASE, 1), config1);
+ SPISlave slave(bus, cs, config1);
SPITransaction spi(slave);
- REQUIRE(bus.out_buf.size() == 0);
+ REQUIRE(bus.getOutBuf().size() == 0);
SECTION("1 byte reg read")
{
- REQUIRE(spi.read(0x05) == bus.in_buf[0]);
+ REQUIRE(spi.read(0x05) == in_data[0]);
REQUIRE_FALSE(bus.isSelected());
- REQUIRE(bus.out_buf.size() == 1);
- REQUIRE(bus.out_buf.back() == 0x85);
+ REQUIRE(bus.getOutBuf().size() == 1);
+ REQUIRE(bus.getOutBuf().back() == 0x85);
- REQUIRE(spi.read(0x05, true) == bus.in_buf[1]);
+ REQUIRE(spi.read(0x05, true) == in_data[1]);
REQUIRE_FALSE(bus.isSelected());
- REQUIRE(bus.out_buf.size() == 2);
- REQUIRE(bus.out_buf.back() == 0x85);
+ REQUIRE(bus.getOutBuf().size() == 2);
+ REQUIRE(bus.getOutBuf().back() == 0x85);
- REQUIRE(spi.read(0x05, false) == bus.in_buf[2]);
+ REQUIRE(spi.read(0x05, false) == in_data[2]);
REQUIRE_FALSE(bus.isSelected());
- REQUIRE(bus.out_buf.size() == 3);
- REQUIRE(bus.out_buf.back() == 0x05);
+ REQUIRE(bus.getOutBuf().size() == 3);
+ REQUIRE(bus.getOutBuf().back() == 0x05);
}
SECTION("multi byte reg read")
@@ -438,29 +464,29 @@ TEST_CASE("SPITransaction - reads")
spi.read(0x05, buf, 0);
REQUIRE_FALSE(bus.isSelected());
- REQUIRE(bus.out_buf.size() == 1);
- REQUIRE(bus.out_buf.back() == 0x85);
+ REQUIRE(bus.getOutBuf().size() == 1);
+ REQUIRE(bus.getOutBuf().back() == 0x85);
REQUIRE(bufcmp(buf, cmp, buf_size));
spi.read(0x05, buf, 3);
REQUIRE_FALSE(bus.isSelected());
- REQUIRE(bus.out_buf.size() == 2);
- REQUIRE(bus.out_buf.back() == 0x85);
- REQUIRE(bufcmp(buf, bus.in_buf.data(), 3));
+ REQUIRE(bus.getOutBuf().size() == 2);
+ REQUIRE(bus.getOutBuf().back() == 0x85);
+ REQUIRE(bufcmp(buf, &in_data[0], 3));
REQUIRE(bufcmp(buf + 3, cmp + 3, buf_size - 3));
spi.read(0x05, buf, 3, true);
REQUIRE_FALSE(bus.isSelected());
- REQUIRE(bus.out_buf.size() == 3);
- REQUIRE(bus.out_buf.back() == 0x85);
- REQUIRE(bufcmp(buf, bus.in_buf.data() + 3, 3));
+ REQUIRE(bus.getOutBuf().size() == 3);
+ REQUIRE(bus.getOutBuf().back() == 0x85);
+ REQUIRE(bufcmp(buf, &in_data[3], 3));
REQUIRE(bufcmp(buf + 3, cmp + 3, buf_size - 3));
spi.read(0x05, buf, 4, false);
REQUIRE_FALSE(bus.isSelected());
- REQUIRE(bus.out_buf.size() == 4);
- REQUIRE(bus.out_buf.back() == 0x05);
- REQUIRE(bufcmp(buf, bus.in_buf.data() + 6, 4));
+ REQUIRE(bus.getOutBuf().size() == 4);
+ REQUIRE(bus.getOutBuf().back() == 0x05);
+ REQUIRE(bufcmp(buf,&in_data[6], 4));
REQUIRE(bufcmp(buf + 4, cmp + 4, buf_size - 4));
}
@@ -472,13 +498,13 @@ TEST_CASE("SPITransaction - reads")
spi.read(buf, 0);
REQUIRE_FALSE(bus.isSelected());
- REQUIRE(bus.out_buf.size() == 0);
+ REQUIRE(bus.getOutBuf().size() == 0);
REQUIRE(bufcmp(buf, cmp, buf_size));
spi.read(buf, 3);
REQUIRE_FALSE(bus.isSelected());
- REQUIRE(bus.out_buf.size() == 0);
- REQUIRE(bufcmp(buf, bus.in_buf.data(), 3));
+ REQUIRE(bus.getOutBuf().size() == 0);
+ REQUIRE(bufcmp(buf, &in_data[0], 3));
REQUIRE(bufcmp(buf + 3, cmp + 3, buf_size - 3));
}
}
@@ -486,20 +512,21 @@ TEST_CASE("SPITransaction - reads")
TEST_CASE("SPITransaction - transfer")
{
- MockSPIBus bus;
-
- bus.in_buf = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-
SPIBusConfig config1;
config1.mode = SPIMode::MODE1;
config1.clock_div = SPIClockDivider::DIV32;
- bus.expected_config = config1;
+ MockSPIBus bus(config1);
+ MockGpioPin cs;
+
+ uint8_t data[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+
+ bus.push(data, 10);
SECTION("Transaction")
{
- SPISlave slave(bus, GpioPin(GPIOA_BASE, 1), config1);
+ SPISlave slave(bus, cs, config1);
SPITransaction spi(slave);
const int buf_size = 7;
@@ -508,14 +535,14 @@ TEST_CASE("SPITransaction - transfer")
spi.transfer(buf, 0);
REQUIRE_FALSE(bus.isSelected());
- REQUIRE(bus.out_buf.size() == 0);
+ REQUIRE(bus.getOutBuf().size() == 0);
REQUIRE(bufcmp(buf, cmp, buf_size));
spi.transfer(buf, 4);
REQUIRE_FALSE(bus.isSelected());
- REQUIRE(bus.out_buf.size() == 4);
- REQUIRE(bufcmp(buf, bus.in_buf.data(), 4));
- REQUIRE(bufcmp(cmp, bus.out_buf.data(), 4));
+ REQUIRE(bus.getOutBuf().size() == 4);
+ REQUIRE(bufcmp(buf, &data[0], 4));
+ REQUIRE(bufcmp(cmp, bus.getOutBuf().data(), 4));
REQUIRE(bufcmp(buf + 4, cmp + 4, buf_size - 4));
}
}
\ No newline at end of file
diff --git a/src/tests/catch/xbee/MockXbeeSPIBus.h b/src/tests/catch/xbee/MockXbeeSPIBus.h
new file mode 100644
index 000000000..2c23b4fd8
--- /dev/null
+++ b/src/tests/catch/xbee/MockXbeeSPIBus.h
@@ -0,0 +1,172 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <deque>
+#include <functional>
+#include <memory>
+
+#include "drivers/Xbee/APIFrameParser.h"
+#include "drivers/Xbee/APIFrames.h"
+#include "drivers/spi/test/MockSPIBus.h"
+#include "utils/testutils/MockGpioPin.h"
+
+using std::deque;
+using std::function;
+using std::unique_ptr;
+
+class MockXbeeSPIBus : public MockSPIBus
+{
+public:
+ MockXbeeSPIBus(SPIBusConfig expected_config, MockGpioPin& attn)
+ : MockSPIBus(expected_config), attn(attn)
+ {
+ }
+
+ void registerCallback(function<void(MockGpioPin&)> callback)
+ {
+ this->callback = callback;
+ }
+
+ void removeCallback() { this->callback = nullptr; }
+
+ void pushApiFrame(Xbee::APIFrame& api)
+ {
+ unique_ptr<uint8_t> bytes(new uint8_t[Xbee::MAX_API_FRAME_SIZE]);
+
+ size_t len = api.toBytes(bytes.get());
+ push(bytes.get(), len);
+ }
+
+ deque<Xbee::APIFrame> getParsedFrames()
+ {
+ Lock<FastMutex> l(mutex);
+ return parsed_frames;
+ }
+
+ /**
+ * @brief Wether to generate a tx_status responde upon receiving a tx
+ * request
+ *
+ */
+ void setRespondWithTxStatus(bool respond, uint8_t delivery_status = 0)
+ {
+ tx_status_delivery_status = delivery_status;
+ respond_with_tx_status = respond;
+ }
+
+protected:
+ void _push(uint8_t* data, size_t len) override
+ {
+ MockSPIBus::_push(data, len);
+
+ assertATTN();
+ }
+
+ uint8_t _read() override
+ {
+ uint8_t r = MockSPIBus::_read();
+
+ if (in_buf_pos_cntr == in_buf.size())
+ {
+ resetATTN();
+ }
+
+ return r;
+ }
+
+ void _write(uint8_t byte) override
+ {
+ MockSPIBus::_write(byte);
+
+ Xbee::APIFrameParser::ParseResult res =
+ parser.parse(byte, &parsing_frame);
+
+ if (res == Xbee::APIFrameParser::ParseResult::SUCCESS)
+ {
+ parsed_frames.push_back(parsing_frame);
+
+ if (parsing_frame.frame_type == Xbee::FTYPE_TX_REQUEST &&
+ respond_with_tx_status)
+ {
+ Xbee::TXRequestFrame* tx_req =
+ parsing_frame.toFrameType<Xbee::TXRequestFrame>();
+ Xbee::TXStatusFrame tx_stat;
+
+ tx_stat.setFrameID(tx_req->getFrameID());
+ tx_stat.setDeliveryStatus(tx_status_delivery_status);
+ tx_stat.setDiscoveryStatus(0);
+ tx_stat.setTransmitRetryCount(0);
+ tx_stat.calcChecksum();
+
+ _pushApiFrame(tx_stat);
+ }
+ }
+ else if (res == Xbee::APIFrameParser::ParseResult::FAIL)
+ {
+ printf("[MockXbeeSPI] Failed parsing frame written to SPI bus\n");
+ }
+ }
+
+private:
+ void _pushApiFrame(Xbee::APIFrame& api)
+ {
+ uint8_t* bytes = new uint8_t[Xbee::MAX_API_FRAME_SIZE];
+ size_t len = api.toBytes(bytes);
+ _push(bytes, len);
+
+ delete[] bytes;
+ }
+
+ void assertATTN()
+ {
+ if (attn.value() != 0)
+ {
+ attn.low();
+ if (callback)
+ callback(attn);
+ }
+ }
+
+ void resetATTN()
+ {
+ if (attn.value() == 0)
+ {
+ attn.high();
+ if (callback)
+ callback(attn);
+ }
+ }
+
+ deque<Xbee::APIFrame> parsed_frames;
+
+ Xbee::APIFrameParser parser;
+ Xbee::APIFrame parsing_frame;
+
+ function<void(MockGpioPin&)> callback;
+ MockGpioPin attn;
+
+ bool respond_with_tx_status = true;
+ uint8_t tx_status_delivery_status = 0;
+};
\ No newline at end of file
diff --git a/src/tests/catch/xbee/test-xbee-driver.cpp b/src/tests/catch/xbee/test-xbee-driver.cpp
new file mode 100644
index 000000000..71aa6b54e
--- /dev/null
+++ b/src/tests/catch/xbee/test-xbee-driver.cpp
@@ -0,0 +1,415 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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 STANDALONE_CATCH1_TEST
+#include "../catch-tests-entry.cpp"
+#endif
+#include <ctime>
+#include <future>
+#include <memory>
+
+#include "MockXbeeSPIBus.h"
+#include "drivers/Xbee/Xbee.h"
+#include "drivers/spi/SPIDriver.h"
+#include "utils/testutils/catch.hpp"
+
+using namespace Xbee;
+using std::async;
+using std::unique_ptr;
+
+using uint8_ptr = unique_ptr<uint8_t>;
+
+/**
+ * @brief Generates a sequence of incremental bytes
+ */
+uint8_ptr incrementalBytes(size_t length, uint8_t start_from = 0)
+{
+ uint8_ptr bytes(new uint8_t[length]);
+
+ for (size_t i = 0; i < length; i++)
+ {
+ bytes.get()[i] = (start_from + i) % 256;
+ }
+
+ return bytes;
+}
+
+class XbeeWrapper
+{
+public:
+ XbeeWrapper(unsigned int tx_timeout)
+ : bus(new MockXbeeSPIBus(xbee_cfg, attn)),
+ xbee(new Xbee::Xbee(*bus.get(), xbee_cfg, cs, attn, rst, tx_timeout))
+ {
+ attn.high();
+ }
+
+ MockGpioPin cs;
+ MockGpioPin attn;
+
+ MockGpioPin rst;
+
+ SPIBusConfig xbee_cfg{};
+
+ unique_ptr<MockXbeeSPIBus> bus;
+ unique_ptr<Xbee::Xbee> xbee;
+};
+
+TEST_CASE("[Xbee] Test xbee.send(...) by itself")
+{
+ uint8_ptr pkt = incrementalBytes(Xbee::MAX_PACKET_PAYLOAD_LENGTH + 1);
+ uint8_ptr pkt_orig = incrementalBytes(Xbee::MAX_PACKET_PAYLOAD_LENGTH + 1);
+
+ XbeeWrapper wrap(DEFAULT_TX_TIMEOUT);
+
+ SECTION("Middle payload length")
+ {
+ REQUIRE(wrap.xbee->send(pkt.get(), 128));
+ REQUIRE(wrap.bus->getParsedFrames().size() > 0);
+
+ REQUIRE(wrap.bus->getParsedFrames().front().frame_type ==
+ Xbee::FTYPE_TX_REQUEST);
+
+ TXRequestFrame tx_req = *wrap.bus->getParsedFrames()
+ .front()
+ .toFrameType<Xbee::TXRequestFrame>();
+
+ REQUIRE(tx_req.getRFDataLength() == 128);
+ REQUIRE(memcmp(tx_req.getRFDataPointer(), pkt_orig.get(), 128) == 0);
+ }
+
+ SECTION("Max payload length")
+ {
+ REQUIRE(wrap.xbee->send(pkt.get(), Xbee::MAX_PACKET_PAYLOAD_LENGTH));
+ REQUIRE(wrap.bus->getParsedFrames().size() > 0);
+
+ REQUIRE(wrap.bus->getParsedFrames().front().frame_type ==
+ Xbee::FTYPE_TX_REQUEST);
+
+ TXRequestFrame tx_req = *wrap.bus->getParsedFrames()
+ .front()
+ .toFrameType<Xbee::TXRequestFrame>();
+
+ REQUIRE(tx_req.getRFDataLength() == Xbee::MAX_PACKET_PAYLOAD_LENGTH);
+ REQUIRE(memcmp(tx_req.getRFDataPointer(), pkt_orig.get(),
+ Xbee::MAX_PACKET_PAYLOAD_LENGTH) == 0);
+ }
+
+ SECTION("Oversize payload")
+ {
+ REQUIRE_FALSE(
+ wrap.xbee->send(pkt.get(), Xbee::MAX_PACKET_PAYLOAD_LENGTH + 1));
+ REQUIRE(wrap.bus->getParsedFrames().size() == 0);
+ }
+
+ SECTION("No payload")
+ {
+ REQUIRE_FALSE(wrap.xbee->send(pkt.get(), 0));
+ REQUIRE(wrap.bus->getParsedFrames().size() == 0);
+ }
+
+ SECTION("Send error")
+ {
+ wrap.bus->setRespondWithTxStatus(true, DELS_NO_SPECTRUM_AVAILABLE);
+
+ REQUIRE_FALSE(
+ wrap.xbee->send(pkt.get(), Xbee::MAX_PACKET_PAYLOAD_LENGTH));
+ }
+
+ SECTION("TX status timeout")
+ {
+ wrap.bus->setRespondWithTxStatus(false);
+
+ long long start = miosix::getTick();
+
+ REQUIRE_FALSE(
+ wrap.xbee->send(pkt.get(), Xbee::MAX_PACKET_PAYLOAD_LENGTH));
+
+ // Should not have returned until the timeout has expired
+ REQUIRE(miosix::getTick() >= start + DEFAULT_TX_TIMEOUT);
+ }
+}
+
+TEST_CASE("[Xbee] Test xbee.receive(...) by itself")
+{
+ uint8_ptr rx_buf(new uint8_t[MAX_PACKET_PAYLOAD_LENGTH]);
+
+ uint8_ptr pkt = incrementalBytes(Xbee::MAX_PACKET_PAYLOAD_LENGTH);
+
+ XbeeWrapper wrap(DEFAULT_TX_TIMEOUT);
+
+ RXPacketFrame rx;
+ rx.setRXDataLength(MAX_PACKET_PAYLOAD_LENGTH);
+ memcpy(rx.getRXDataPointer(), pkt.get(), MAX_PACKET_PAYLOAD_LENGTH);
+ rx.setReceiveOptions(RO_POINT_MULTIPOINT);
+ rx.setSourceAddress(0x1122334455667788);
+
+ SECTION("RX buffer bigger than RX packet payload")
+ {
+ rx.setRXDataLength(50);
+ rx.calcChecksum();
+
+ wrap.bus->pushApiFrame(rx);
+
+ REQUIRE(wrap.xbee->receive(rx_buf.get(), MAX_PACKET_PAYLOAD_LENGTH) ==
+ 50);
+
+ REQUIRE(memcmp(rx_buf.get(), pkt.get(), 50) == 0);
+ }
+
+ SECTION("RX buffer smaller than RX packet payload")
+ {
+ rx.setRXDataLength(130);
+ rx.calcChecksum();
+
+ wrap.bus->pushApiFrame(rx);
+
+ REQUIRE(wrap.xbee->receive(rx_buf.get(), 50) == 50);
+ REQUIRE(wrap.xbee->receive(rx_buf.get() + 50, 50) == 50);
+ REQUIRE(wrap.xbee->receive(rx_buf.get() + 100, 50) == 30);
+
+ REQUIRE(memcmp(rx_buf.get(), pkt.get(), 130) == 0);
+ }
+
+ SECTION("RX buffer == 1")
+ {
+ rx.setRXDataLength(30);
+ rx.calcChecksum();
+
+ wrap.bus->pushApiFrame(rx);
+
+ for (int i = 0; i < 30; i++)
+ {
+ REQUIRE(wrap.xbee->receive(rx_buf.get() + i, 1) == 1);
+ }
+
+ REQUIRE(memcmp(rx_buf.get(), pkt.get(), 30) == 0);
+ }
+
+ SECTION("RX packet with wrong checksum")
+ {
+ rx.setRXDataLength(20);
+ rx.calcChecksum();
+ wrap.bus->pushApiFrame(rx);
+
+ rx.setRXDataLength(21);
+ rx.calcChecksum();
+ rx.setRXDataLength(20); // Now the checksum is wrong
+ wrap.bus->pushApiFrame(rx);
+
+ // Only receive one of the two frames
+ REQUIRE(wrap.xbee->receive(rx_buf.get(), 50) == 20);
+
+ auto dont_care = async(std::launch::async, [&]() {
+ miosix::Thread::sleep(1000);
+ wrap.xbee->wakeReceiver(true);
+ });
+
+ // This should block until we force it to return
+ REQUIRE(wrap.xbee->receive(rx_buf.get(), 50) == -1);
+ }
+
+ SECTION("receive() blocks until an interrupt is received")
+ {
+ rx.setRXDataLength(20);
+ rx.calcChecksum();
+
+ // Send an rx frame in one second
+ auto dont_care = async(std::launch::async, [&]() {
+ miosix::Thread::sleep(1000);
+ wrap.bus->pushApiFrame(rx);
+ wrap.xbee->wakeReceiver();
+ });
+
+ long long start = miosix::getTick();
+ REQUIRE(wrap.xbee->receive(rx_buf.get(), 50) == 20);
+
+ // Should not have returned before we sent the rx frame
+ REQUIRE(miosix::getTick() >= start + 1000);
+ }
+}
+
+TEST_CASE("[Xbee] Receive while sending")
+{
+ uint8_ptr rx_buf(new uint8_t[MAX_PACKET_PAYLOAD_LENGTH]);
+
+ uint8_ptr pkt_rx = incrementalBytes(Xbee::MAX_PACKET_PAYLOAD_LENGTH);
+ uint8_ptr pkt_tx = incrementalBytes(Xbee::MAX_PACKET_PAYLOAD_LENGTH);
+
+ XbeeWrapper wrap(1000);
+ wrap.bus->setRespondWithTxStatus(true, 0);
+
+ RXPacketFrame rx;
+ rx.setRXDataLength(MAX_PACKET_PAYLOAD_LENGTH);
+ memcpy(rx.getRXDataPointer(), pkt_rx.get(), MAX_PACKET_PAYLOAD_LENGTH);
+ rx.setReceiveOptions(RO_POINT_MULTIPOINT);
+ rx.setSourceAddress(0x1122334455667788);
+
+ int num_rx_while_tx = 0;
+
+ wrap.xbee->setOnFrameReceivedListener([&](APIFrame& api) {
+ if (api.frame_type == FTYPE_RX_PACKET_FRAME)
+ {
+ ++num_rx_while_tx;
+ }
+ });
+
+ SECTION("2 RX packet smaller than one TX packet")
+ {
+ rx.setRXDataLength(50);
+ rx.calcChecksum();
+
+ wrap.bus->pushApiFrame(rx);
+ wrap.bus->pushApiFrame(rx);
+
+ REQUIRE(wrap.xbee->send(pkt_tx.get(), 150));
+
+ REQUIRE(num_rx_while_tx == 2);
+
+ size_t rx_len = 0;
+ while (rx_len < 100)
+ {
+ rx_len += wrap.xbee->receive(rx_buf.get() + rx_len, 200);
+ }
+
+ REQUIRE(rx_len == 100);
+ REQUIRE(memcmp(rx_buf.get(), pkt_rx.get(), 50) == 0);
+ REQUIRE(memcmp(rx_buf.get() + 50, pkt_rx.get(), 50) == 0);
+ }
+
+ SECTION("More RX packets than buffer size: drop the oldest")
+ {
+ rx.setRXDataLength(10);
+
+ // 1 packet too many
+ unsigned int numpkts = RX_FRAMES_BUF_SIZE + 1;
+ for (unsigned int i = 0; i < numpkts; i++)
+ {
+ memcpy(rx.getRXDataPointer(), pkt_rx.get() + i, 10);
+ rx.calcChecksum();
+ wrap.bus->pushApiFrame(rx);
+ }
+
+ // Should receive all the packets while sending
+ REQUIRE(wrap.xbee->send(pkt_tx.get(), MAX_PACKET_PAYLOAD_LENGTH));
+
+ REQUIRE(num_rx_while_tx == numpkts);
+
+ // Only receive the last three packets
+ size_t rx_len = 0;
+ while (rx_len < (numpkts - 1) * 10)
+ {
+ rx_len += wrap.xbee->receive(rx_buf.get() + rx_len, 200);
+ }
+
+ REQUIRE(rx_len == (numpkts - 1) * 10);
+
+ for (unsigned int i = 0; i < numpkts - 1; i++)
+ {
+ REQUIRE(memcmp(rx_buf.get() + i * 10, pkt_rx.get() + i + 1, 10) ==
+ 0);
+ }
+ }
+
+ SECTION("2 RX packet smaller than one TX packet, 1 byte receive")
+ {
+ rx.setRXDataLength(50);
+ rx.calcChecksum();
+
+ wrap.bus->pushApiFrame(rx);
+ wrap.bus->pushApiFrame(rx);
+
+ REQUIRE(wrap.xbee->send(pkt_tx.get(), 150));
+
+ REQUIRE(num_rx_while_tx == 2);
+
+ for (int i = 0; i < 50; i++)
+ {
+ REQUIRE(wrap.xbee->receive(rx_buf.get() + i, 1) == 1);
+ }
+ REQUIRE(memcmp(rx_buf.get(), pkt_rx.get(), 50) == 0);
+
+ for (int i = 0; i < 50; i++)
+ {
+ REQUIRE(wrap.xbee->receive(rx_buf.get() + i, 1) == 1);
+ }
+ REQUIRE(memcmp(rx_buf.get(), pkt_rx.get(), 50) == 0);
+ }
+}
+
+TEST_CASE("[Xbee] Test Xbee::sendAtCommand(...)")
+{
+ XbeeWrapper wrap(DEFAULT_TX_TIMEOUT);
+
+ SECTION("AT Command, no response required")
+ {
+ wrap.xbee->sendATCommand("AB");
+
+ REQUIRE(wrap.bus->getParsedFrames().size() > 0);
+ REQUIRE(wrap.bus->getParsedFrames()[0].frame_type == FTYPE_AT_COMMAND);
+ }
+
+ SECTION("AT Command, response required but not received")
+ {
+ ATCommandResponseFrame response;
+ long long start = miosix::getTick();
+ REQUIRE_FALSE(
+ wrap.xbee->sendATCommand("AB", &response, nullptr, 0, 1000));
+ REQUIRE(miosix::getTick() >= start + 1000);
+
+ REQUIRE(wrap.bus->getParsedFrames().size() > 0);
+ REQUIRE(wrap.bus->getParsedFrames()[0].frame_type == FTYPE_AT_COMMAND);
+ }
+
+ SECTION("AT Command, response required and received")
+ {
+ // Padding required in order to not receive the ATCommandResponse
+ // frame too early in testing
+ RXPacketFrame padding;
+ padding.setRXDataLength(50);
+ padding.calcChecksum();
+
+ ATCommandResponseFrame resp;
+ resp.setATCommand("AB");
+ resp.setFrameID(1);
+ resp.setCommandDataSize(1);
+ resp.getCommandDataPointer()[0] = 0xAB;
+ resp.calcChecksum();
+
+ wrap.bus->pushApiFrame(padding);
+ wrap.bus->pushApiFrame(resp);
+
+ ATCommandResponseFrame received_response;
+ long long start = miosix::getTick();
+
+ REQUIRE(wrap.xbee->sendATCommand("AB", &received_response, nullptr, 0,
+ 1000));
+ REQUIRE(miosix::getTick() < start + 1000);
+
+ REQUIRE(wrap.bus->getParsedFrames().size() > 0);
+ REQUIRE(wrap.bus->getParsedFrames()[0].frame_type == FTYPE_AT_COMMAND);
+
+ REQUIRE(received_response.getCommandDataLength() == 1);
+ REQUIRE(received_response.getCommandDataPointer()[0] == 0xAB);
+ }
+}
\ No newline at end of file
diff --git a/src/tests/catch/xbee/test-xbee-parser.cpp b/src/tests/catch/xbee/test-xbee-parser.cpp
new file mode 100644
index 000000000..10c54ae87
--- /dev/null
+++ b/src/tests/catch/xbee/test-xbee-parser.cpp
@@ -0,0 +1,575 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+// Disable asserts
+#define NDEBUG
+
+#include <cstdio>
+
+#ifdef STANDALONE_CATCH1_TEST
+#include "../catch-tests-entry.cpp"
+#endif
+
+#include <cstdio>
+#include <cstring>
+
+#include "drivers/Xbee/APIFrameParser.h"
+#include "utils/testutils/catch.hpp"
+
+using namespace Xbee;
+
+/**
+ * @brief Calculates the checksum for a sequence of bytes representing an API
+ * frame.
+ *
+ * @param frame bytes (including start delimiter, and checksum set to 0)
+ * @param frame_size size of the frame, including start delimiter and checksum
+ */
+void calcChecksum(uint8_t* frame, size_t frame_size)
+{
+ frame[frame_size - 1] = 0;
+ for (size_t i = 3; i < frame_size - 1; i++)
+ {
+ frame[frame_size - 1] += frame[i];
+ }
+ frame[frame_size - 1] = 0xFF - frame[frame_size - 1];
+}
+
+void printHex(uint8_t* frame, size_t frame_size)
+{
+ for (size_t i = 0; i < frame_size; i++)
+ {
+ printf("%02X ", frame[i]);
+ }
+ printf("\n");
+}
+
+void printu64(uint64_t v)
+{
+ uint8_t* p = reinterpret_cast<uint8_t*>(&v);
+
+ for (int i = 0; i < 8; i++)
+ {
+ printf("%02X ", p[i]);
+ }
+
+ printf("\n");
+}
+
+void printBuf(uint8_t* buf, size_t size)
+{
+ for (size_t i = 0; i < size; i++)
+ {
+ printf("%02X ", buf[i]);
+ }
+
+ printf("\n");
+}
+
+bool compareAPIFrames(const APIFrame& a, const APIFrame& b)
+{
+ bool result = a.start_del == b.start_del && a.frame_type == b.frame_type &&
+ a.length == b.length && a.checksum == b.checksum;
+
+ return result &&
+ memcmp(a.frame_data, b.frame_data, a.getFrameDataLength()) == 0;
+}
+
+APIFrame parse(APIFrameParser& parser, uint8_t* data, size_t len)
+{
+ APIFrame out;
+ for (size_t i = 0; i < len; i++)
+ {
+ APIFrameParser::ParseResult pres = parser.parse(data[i], &out);
+
+ if (i < len - 1)
+ {
+ CAPTURE(i);
+ REQUIRE(pres == APIFrameParser::ParseResult::PARSING);
+ }
+ else
+ {
+ REQUIRE(pres == APIFrameParser::ParseResult::SUCCESS);
+ }
+ }
+
+ REQUIRE(out.verifyChecksum());
+
+ return out;
+}
+
+template <typename FrameType>
+void testParse(APIFrameParser& parser, FrameType orig)
+{
+ uint8_t bytes[MAX_API_FRAME_SIZE];
+
+ size_t len = orig.toBytes(bytes);
+
+ APIFrame parsed_api = parse(parser, bytes, len);
+
+ REQUIRE(compareAPIFrames(orig, parsed_api));
+}
+
+// Taken from examples in the datasheet
+TEST_CASE("Frame serialization")
+{
+ uint8_t bytes[MAX_API_FRAME_SIZE];
+ APIFrame deserialized;
+
+ SECTION("AT Command")
+ {
+ ATCommandFrame at_orig;
+ at_orig.setATCommand("NH");
+ REQUIRE(memcmp(at_orig.getATCommand(), "NH", 2) == 0);
+
+ at_orig.setFrameID(0x01);
+ REQUIRE(at_orig.getFrameID() == 0x01);
+
+ at_orig.setParameterSize(2);
+ at_orig.getCommandDataPointer()[0] = 0x07;
+ at_orig.getCommandDataPointer()[1] = 0x17;
+
+ REQUIRE(at_orig.getCommandDataLength() == 2);
+ REQUIRE(at_orig.getFrameDataLength() == 5);
+
+ at_orig.calcChecksum();
+
+ uint8_t expected_bytes[] = {0x7E, 0, 6, 0x08, 1,
+ 0x4E, 0x48, 0x07, 0x17, 0x00};
+
+ calcChecksum(expected_bytes, 10);
+
+ size_t len = at_orig.toBytes(bytes);
+
+ REQUIRE(len == 10);
+
+ REQUIRE(memcmp(bytes, expected_bytes, len) == 0);
+
+ REQUIRE(APIFrame::fromBytes(bytes, len, &deserialized));
+ REQUIRE(compareAPIFrames(deserialized, at_orig));
+ }
+
+ SECTION("AT Command, No parameters")
+ {
+ ATCommandFrame at_orig;
+
+ at_orig.setATCommand("NH");
+
+ at_orig.setFrameID(0x01);
+ REQUIRE(at_orig.getFrameID() == 0x01);
+
+ REQUIRE(at_orig.getCommandDataLength() == 0);
+ REQUIRE(at_orig.getFrameDataLength() == 3);
+
+ at_orig.calcChecksum();
+
+ uint8_t expected_bytes[] = {0x7E, 0, 4, 0x08, 1, 0x4E, 0x48, 0x60};
+
+ size_t len = at_orig.toBytes(bytes);
+
+ REQUIRE(len == 8);
+
+ REQUIRE(memcmp(bytes, expected_bytes, len) == 0);
+
+ REQUIRE(APIFrame::fromBytes(bytes, len, &deserialized));
+ REQUIRE(compareAPIFrames(deserialized, at_orig));
+ }
+
+ SECTION("AT Command Response")
+ {
+ ATCommandResponseFrame at_orig;
+ at_orig.setATCommand("BD");
+ REQUIRE(memcmp(at_orig.getATCommand(), "BD", 2) == 0);
+
+ at_orig.setFrameID(0x01);
+ REQUIRE(at_orig.getFrameID() == 0x01);
+
+ at_orig.setCommandDataSize(3);
+ at_orig.getCommandDataPointer()[0] = 0x07;
+ at_orig.getCommandDataPointer()[1] = 0x17;
+ at_orig.getCommandDataPointer()[2] = 0x27;
+
+ REQUIRE(at_orig.getCommandDataLength() == 3);
+ REQUIRE(at_orig.getFrameDataLength() == 7);
+
+ at_orig.setCommandStatus(0x55);
+ REQUIRE(at_orig.getCommandStatus() == 0x55);
+
+ at_orig.calcChecksum();
+
+ uint8_t expected_bytes[] = {0x7E, 0, 8, 0x88, 0x01, 0x42,
+ 0x44, 0x55, 0x07, 0x17, 0x27, 0};
+
+ calcChecksum(expected_bytes, 12);
+
+ size_t len = at_orig.toBytes(bytes);
+ REQUIRE(len == 12);
+
+ REQUIRE(memcmp(bytes, expected_bytes, len) == 0);
+
+ REQUIRE(APIFrame::fromBytes(bytes, len, &deserialized));
+ REQUIRE(compareAPIFrames(deserialized, at_orig));
+ }
+
+ SECTION("AT Command Response, No cmd data")
+ {
+ ATCommandResponseFrame at_orig;
+ at_orig.setATCommand("BD");
+
+ at_orig.setFrameID(0x01);
+ REQUIRE(at_orig.getFrameID() == 0x01);
+
+ REQUIRE(at_orig.getCommandDataLength() == 0);
+ REQUIRE(at_orig.getFrameDataLength() == 4);
+
+ at_orig.setCommandStatus(0x0);
+ REQUIRE(at_orig.getCommandStatus() == 0x00);
+
+ at_orig.calcChecksum();
+
+ uint8_t expected_bytes[] = {0x7E, 0, 5, 0x88, 0x01,
+ 0x42, 0x44, 0x00, 0xF0};
+
+ calcChecksum(expected_bytes, 9);
+
+ size_t len = at_orig.toBytes(bytes);
+ REQUIRE(len == 9);
+
+ REQUIRE(memcmp(bytes, expected_bytes, len) == 0);
+ REQUIRE(APIFrame::fromBytes(bytes, len, &deserialized));
+ REQUIRE(compareAPIFrames(deserialized, at_orig));
+ }
+
+ SECTION("TX Request Frame")
+ {
+ TXRequestFrame tx_orig;
+
+ tx_orig.setFrameID(0x01);
+ REQUIRE(tx_orig.getFrameID() == 0x01);
+
+ tx_orig.setDestAddress(0x0013A200400A0127);
+
+ bool addr_cmp = tx_orig.getDestAddress() == 0x0013A200400A0127;
+ REQUIRE(addr_cmp);
+
+ tx_orig.setBroadcastRadius(0x55);
+ REQUIRE(tx_orig.getBroadcastRadius() == 0x55);
+
+ tx_orig.setTransmitOptions(0x40);
+ REQUIRE(tx_orig.getTrasmitOptions() == 0x40);
+
+ uint8_t* rf_data = tx_orig.getRFDataPointer();
+
+ rf_data[0] = 0x54;
+ rf_data[1] = 0x78;
+ rf_data[2] = 0x44;
+ rf_data[3] = 0x61;
+ rf_data[4] = 0x74;
+ rf_data[5] = 0x61;
+ rf_data[6] = 0x30;
+ rf_data[7] = 0x41;
+
+ tx_orig.setRFDataLength(8);
+
+ REQUIRE(tx_orig.getRFDataLength() == 8);
+ REQUIRE(tx_orig.getFrameDataLength() == 21);
+
+ uint8_t expected_bytes[] = {0x7E, 0, 0x16, 0x10, 0x01, 0x00, 0x13,
+ 0xA2, 0x00, 0x40, 0x0A, 0x01, 0x27, 0xFF,
+ 0xFE, 0x55, 0x40, 0x54, 0x78, 0x44, 0x61,
+ 0x74, 0x61, 0x30, 0x41, 0x00};
+
+ // Datasheet example checksum is wrong
+ calcChecksum(expected_bytes, 26);
+ tx_orig.calcChecksum();
+ size_t len = tx_orig.toBytes(bytes);
+
+ REQUIRE(len == 26);
+
+ REQUIRE(memcmp(bytes, expected_bytes, len) == 0);
+ REQUIRE(APIFrame::fromBytes(bytes, len, &deserialized));
+ REQUIRE(compareAPIFrames(deserialized, tx_orig));
+ }
+
+ SECTION("TX Request Frame - No payload")
+ {
+ TXRequestFrame tx_orig;
+
+ tx_orig.setFrameID(0x01);
+ REQUIRE(tx_orig.getFrameID() == 0x01);
+
+ tx_orig.setDestAddress(0x0013A200400A0127);
+ bool addr_cmp = tx_orig.getDestAddress() == 0x0013A200400A0127;
+ REQUIRE(addr_cmp);
+
+ tx_orig.setBroadcastRadius(0x55);
+ REQUIRE(tx_orig.getBroadcastRadius() == 0x55);
+
+ tx_orig.setTransmitOptions(0x40);
+ REQUIRE(tx_orig.getTrasmitOptions() == 0x40);
+
+ uint8_t* rf_data = tx_orig.getRFDataPointer();
+
+ REQUIRE(tx_orig.getRFDataLength() == 0);
+ REQUIRE(tx_orig.getFrameDataLength() == 13);
+
+ tx_orig.calcChecksum();
+
+ uint8_t expected_bytes[] = {0x7E, 0, 14, 0x10, 0x01, 0x00,
+ 0x13, 0xA2, 0x00, 0x40, 0x0A, 0x01,
+ 0x27, 0xFF, 0xFE, 0x55, 0x40, 0x00};
+
+ calcChecksum(expected_bytes, 18);
+
+ size_t len = tx_orig.toBytes(bytes);
+
+ REQUIRE(len == 18);
+
+ REQUIRE(memcmp(bytes, expected_bytes, len) == 0);
+ REQUIRE(APIFrame::fromBytes(bytes, len, &deserialized));
+ REQUIRE(compareAPIFrames(deserialized, tx_orig));
+ }
+
+ SECTION("Modem status frame")
+ {
+ ModemStatusFrame mod_orig;
+
+ mod_orig.setStatus(0x55);
+
+ mod_orig.calcChecksum();
+
+ uint8_t expected_bytes[] = {0x7E, 0, 2, 0x8A, 0x55, 0x00};
+
+ calcChecksum(expected_bytes, 6);
+
+ size_t len = mod_orig.toBytes(bytes);
+
+ REQUIRE(memcmp(bytes, expected_bytes, len) == 0);
+ REQUIRE(APIFrame::fromBytes(bytes, len, &deserialized));
+ REQUIRE(compareAPIFrames(deserialized, mod_orig));
+ }
+
+ SECTION("TX Status frame")
+ {
+ constexpr size_t frame_size = 11;
+ TXStatusFrame tx_orig{};
+
+ tx_orig.setFrameID(0x47);
+ REQUIRE(tx_orig.getFrameID() == 0x47);
+ tx_orig.setTransmitRetryCount(0x55);
+ REQUIRE(tx_orig.getTransmitRetryCount() == 0x55);
+
+ tx_orig.setDeliveryStatus(0x44);
+ REQUIRE(tx_orig.getDeliveryStatus() == 0x44);
+
+ tx_orig.setDiscoveryStatus(2);
+ REQUIRE(tx_orig.getDiscoveryStatus() == 2);
+
+ tx_orig.calcChecksum();
+
+ uint8_t expected_bytes[] = {0x7E, 0, 0x07, 0x8B, 0x47, 0xFF,
+ 0xFE, 0x55, 0x44, 0x02, 0x00};
+
+ calcChecksum(expected_bytes, 11);
+
+ size_t len = tx_orig.toBytes(bytes);
+ REQUIRE(len == 11);
+
+ REQUIRE(memcmp(bytes, expected_bytes, len) == 0);
+ REQUIRE(APIFrame::fromBytes(bytes, len, &deserialized));
+ REQUIRE(compareAPIFrames(deserialized, tx_orig));
+ }
+
+ SECTION("RX Packet frame")
+ {
+ RXPacketFrame rx;
+
+ rx.setSourceAddress(0x0013A20040522BAA);
+ bool addr_cmp = rx.getSourceAddress() == 0x0013A20040522BAA;
+ REQUIRE(addr_cmp);
+
+ rx.setReceiveOptions(0x01);
+ REQUIRE(rx.getReceiveOptions() == 0x01);
+
+ uint8_t* rf_data = rx.getRXDataPointer();
+
+ rf_data[0] = 0x52;
+ rf_data[1] = 0x78;
+ rf_data[2] = 0x44;
+ rf_data[3] = 0x61;
+ rf_data[4] = 0x74;
+ rf_data[5] = 0x61;
+
+ rx.setRXDataLength(6);
+ REQUIRE(rx.getRXDataLength() == 6);
+ REQUIRE(rx.getFrameDataLength() == 0x11);
+
+ rx.calcChecksum();
+
+ uint8_t expected_bytes[] = {
+ 0x7E, 0, 0x12, 0x90, 0x00, 0x13, 0xA2, 0x00, 0x40, 0x52, 0x2B,
+ 0xAA, 0xFF, 0xFE, 0x01, 0x52, 0x78, 0x44, 0x61, 0x74, 0x61, 0x11};
+
+ // calcChecksum(expected_bytes, frame_size);
+
+ size_t len = rx.toBytes(bytes);
+
+ REQUIRE(len == 22);
+
+ REQUIRE(memcmp(bytes, expected_bytes, len) == 0);
+ REQUIRE(APIFrame::fromBytes(bytes, len, &deserialized));
+ REQUIRE(compareAPIFrames(deserialized, rx));
+ }
+
+ SECTION("RX Packet frame - no payload")
+ {
+ RXPacketFrame rx;
+
+ rx.setSourceAddress(0x0013A20040522BAA);
+ bool addr_cmp = rx.getSourceAddress() == 0x0013A20040522BAA;
+ REQUIRE(addr_cmp);
+
+ rx.setReceiveOptions(0x01);
+ REQUIRE(rx.getReceiveOptions() == 0x01);
+
+ REQUIRE(rx.getRXDataLength() == 0);
+ REQUIRE(rx.getFrameDataLength() == 11);
+
+ rx.calcChecksum();
+
+ uint8_t expected_bytes[] = {0x7E, 0, 12, 0x90, 0x00, 0x13,
+ 0xA2, 0x00, 0x40, 0x52, 0x2B, 0xAA,
+ 0xFF, 0xFE, 0x01, 0x00};
+
+ calcChecksum(expected_bytes, 16);
+
+ size_t len = rx.toBytes(bytes);
+
+ REQUIRE(len == 16);
+
+ REQUIRE(memcmp(bytes, expected_bytes, len) == 0);
+
+ memset(deserialized.frame_data, 0, FRAME_DATA_SIZE);
+ REQUIRE(APIFrame::fromBytes(bytes, len, &deserialized));
+ REQUIRE(compareAPIFrames(deserialized, rx));
+ }
+}
+
+TEST_CASE("Frame parsing")
+{
+ APIFrameParser parser;
+
+ SECTION("RXPacketFrame Parsing")
+ {
+ RXPacketFrame rx;
+ rx.setSourceAddress(0x0013A20040522BAA);
+ rx.setReceiveOptions(0x01);
+ uint8_t* rf_data = rx.getRXDataPointer();
+
+ rf_data[0] = 0x52;
+ rf_data[1] = 0x78;
+ rf_data[2] = 0x44;
+ rf_data[3] = 0x61;
+ rf_data[4] = 0x74;
+ rf_data[5] = 0x61;
+
+ rx.setRXDataLength(6);
+ rx.calcChecksum();
+
+ // Parse 3 times the same frame
+ testParse(parser, rx);
+ testParse(parser, rx);
+ testParse(parser, rx);
+ }
+}
+
+TEST_CASE("Parser edge cases")
+{
+ APIFrameParser parser;
+
+ SECTION("Wrong checksum")
+ {
+ RXPacketFrame rx;
+ rx.setSourceAddress(0x0013A20040522BAA);
+ rx.setReceiveOptions(0x01);
+ uint8_t* rf_data = rx.getRXDataPointer();
+
+ rf_data[0] = 0x52;
+ rf_data[1] = 0x78;
+ rf_data[2] = 0x44;
+ rf_data[3] = 0x61;
+ rf_data[4] = 0x74;
+ rf_data[5] = 0x61;
+
+ rx.setRXDataLength(6);
+ rx.calcChecksum();
+
+ uint8_t bytes[MAX_API_FRAME_SIZE];
+
+ size_t len = rx.toBytes(bytes);
+
+ bytes[len - 1] += 1;
+
+ APIFrame out;
+ for (size_t i = 0; i < len; i++)
+ {
+ APIFrameParser::ParseResult res = parser.parse(bytes[i], &out);
+ REQUIRE_FALSE(res == APIFrameParser::ParseResult::SUCCESS);
+
+ if (i == len - 1)
+ {
+ REQUIRE(res == APIFrameParser::ParseResult::FAIL);
+ }
+ }
+ }
+
+ SECTION("Wrong start delimiter")
+ {
+ RXPacketFrame rx;
+ rx.start_del = 0x55;
+ rx.setSourceAddress(0x0013A20040522BAA);
+ rx.setReceiveOptions(0x01);
+ uint8_t* rf_data = rx.getRXDataPointer();
+
+ rf_data[0] = 0x52;
+ rf_data[1] = 0x78;
+ rf_data[2] = 0x44;
+ rf_data[3] = 0x61;
+ rf_data[4] = 0x74;
+ rf_data[5] = 0x61;
+
+ rx.setRXDataLength(6);
+ rx.calcChecksum();
+
+ uint8_t bytes[MAX_API_FRAME_SIZE];
+
+ size_t len = rx.toBytes(bytes);
+
+ APIFrame out;
+ for (size_t i = 0; i < len; i++)
+ {
+ APIFrameParser::ParseResult res = parser.parse(bytes[i], &out);
+ REQUIRE(res == APIFrameParser::ParseResult::IDLE);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/tests/drivers/test-mavlink.cpp b/src/tests/drivers/test-mavlink.cpp
index e15a022ee..1cfd99181 100644
--- a/src/tests/drivers/test-mavlink.cpp
+++ b/src/tests/drivers/test-mavlink.cpp
@@ -20,7 +20,13 @@
* THE SOFTWARE.
*/
+// Ignore warnings, as we don't want to change third party generated files to
+// fix them
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-align"
+#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
#include <libs/mavlink_skyward_lib/mavlink_lib/r2a/mavlink.h>
+#pragma GCC diagnostic pop
#include <Common.h>
#include <drivers/gamma868/Gamma868.h>
diff --git a/src/tests/drivers/xbee/EnergyScanData.h b/src/tests/drivers/xbee/EnergyScanData.h
new file mode 100644
index 000000000..68e672e27
--- /dev/null
+++ b/src/tests/drivers/xbee/EnergyScanData.h
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <array>
+#include <string>
+#include <ostream>
+
+using std::array;
+using std::string;
+using std::to_string;
+
+struct EnergyScanData
+{
+ long long timestamp;
+ int channel_data[30];
+
+ EnergyScanData() = default;
+
+ EnergyScanData(long long ts, array<int, 30> scan)
+ {
+ for(int i = 0; i < 30; i++)
+ {
+ channel_data[i] = scan[i];
+ }
+ }
+
+ static string header()
+ {
+ string out = "timestamp";
+ for(int i = 0; i < 30; i++)
+ {
+ out += ",channel_" + to_string(i);
+ }
+ return out + "\n";
+ }
+
+ void print(std::ostream& os) const
+ {
+ os << timestamp;
+
+ for(int i = 0; i < 30; i++)
+ {
+ os << "," << channel_data[i];
+ }
+
+ os << "\n";
+ }
+};
\ No newline at end of file
diff --git a/src/tests/drivers/xbee/Mark.h b/src/tests/drivers/xbee/Mark.h
new file mode 100644
index 000000000..52b3c9be1
--- /dev/null
+++ b/src/tests/drivers/xbee/Mark.h
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <string>
+#include <ostream>
+
+using std::string;
+/**
+ * @brief Used to mark a specific instant in the log
+ */
+struct Mark
+{
+ long long timestamp;
+ unsigned int seq;
+
+ static string header()
+ {
+ return "timestamp,seq\n";
+ }
+
+ void print(std::ostream& os) const
+ {
+ os << timestamp << "," << seq << "\n";
+ }
+};
\ No newline at end of file
diff --git a/src/tests/drivers/xbee/XbeeTestData.h b/src/tests/drivers/xbee/XbeeTestData.h
new file mode 100644
index 000000000..3416b48e4
--- /dev/null
+++ b/src/tests/drivers/xbee/XbeeTestData.h
@@ -0,0 +1,153 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <ostream>
+#include <array>
+#include <string>
+
+#include "math/Stats.h"
+
+using std::array;
+using std::string;
+using std::to_string;
+
+struct XbeeConfig
+{
+ long long timestamp;
+ bool tx_enabled = false;
+ uint16_t packet_size = 256;
+ unsigned int send_interval = 0;
+ bool freq_hop = true;
+ bool data_rate_80k = false;
+
+ void print()
+ {
+ printf("+++XBee configuration+++\n\n");
+ printf("Tx: %s, pkt: %u B, int: %d ms\n",
+ tx_enabled ? "enabled" : "disabled", packet_size, send_interval);
+ printf("Freq hop: %s, data rate: %s \n", freq_hop ? "on" : "off",
+ data_rate_80k ? "80 kbps" : "10 kbps");
+ }
+
+ static string header()
+ {
+ return "timestamp,tx_enabled,packet_size,send_interval,freq_hop,data_"
+ "rate_80k\n";
+ }
+
+ void print(std::ostream& os) const
+ {
+ os << timestamp << "," << tx_enabled << "," << packet_size << ","
+ << send_interval << "," << freq_hop << "," << data_rate_80k << "\n";
+ }
+};
+
+struct TxData
+{
+ long long timestamp = 0LL;
+ unsigned int packet_size = 0;
+ unsigned int time_since_last_send = 0;
+ unsigned int time_to_send = 0;
+ unsigned int tx_success_counter = 0;
+ unsigned int tx_fail_counter = 0;
+
+ static string header()
+ {
+ return "timestamp,packet_size,time_since_last_send,time_to_send,tx_"
+ "success_cnt,tx_fail_cnt\n";
+ }
+
+ void print(std::ostream& os) const
+ {
+ os << timestamp << "," << packet_size << "," << time_since_last_send
+ << "," << time_to_send << "," << tx_success_counter << ","
+ << tx_fail_counter << "\n";
+ }
+};
+
+struct RxData
+{
+ long long timestamp;
+ size_t pkt_size = 0;
+ long long last_packet_timestamp = 0;
+ int RSSI = 0;
+ unsigned int rcv_count = 0;
+ unsigned int packets_lost = 0;
+ unsigned int rcv_errors = 0;
+ unsigned int rcv_wrong_payload = 0;
+
+ static string header()
+ {
+ return "timestamp,packet_size,last_packet_timestamp,RSSI,rcv_cnt,rcv_errors,"
+ "rcv_wrong\n";
+ }
+
+ void print(std::ostream& os) const
+ {
+ os << timestamp << "," << pkt_size << "," << last_packet_timestamp << ","
+ << RSSI << "," << rcv_count << "," << rcv_errors << ","
+ << rcv_wrong_payload << "\n";
+ }
+};
+
+struct EnergyScanData
+{
+ long long timestamp;
+ int channel_data[30];
+
+ EnergyScanData() = default;
+
+ EnergyScanData(long long ts, array<int, 30> scan)
+ {
+ for(int i = 0; i < 30; i++)
+ {
+ channel_data[i] = scan[i];
+ }
+
+ timestamp = ts;
+ }
+
+ static string header()
+ {
+ string out = "timestamp";
+ for(int i = 0; i < 30; i++)
+ {
+ out += ",channel_" + to_string(i);
+ }
+ return out + "\n";
+ }
+
+ void print(std::ostream& os) const
+ {
+ os << timestamp;
+
+ for(int i = 0; i < 30; i++)
+ {
+ os << "," << channel_data[i];
+ }
+
+ os << "\n";
+ }
+};
\ No newline at end of file
diff --git a/src/tests/drivers/xbee/XbeeTransceiver.h b/src/tests/drivers/xbee/XbeeTransceiver.h
new file mode 100644
index 000000000..82000b9c9
--- /dev/null
+++ b/src/tests/drivers/xbee/XbeeTransceiver.h
@@ -0,0 +1,424 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <arch/common/drivers/stm32_hardware_rng.h>
+#include <miosix.h>
+
+#include <functional>
+
+#include "ActiveObject.h"
+#include "XbeeTestData.h"
+#include "drivers/Xbee/APIFramesLog.h"
+#include "drivers/Xbee/Xbee.h"
+#include "logger/Logger.h"
+#include "utils/testutils/ThroughputCalculator.h"
+
+using miosix::getTick;
+using std::bind;
+using std::function;
+
+static constexpr size_t RCV_BUF_SIZE = 256;
+static constexpr uint32_t PACKET_FIRST_INT = 0xF0E1C2B3;
+
+/**
+ * @brief Repeats the provided 32 bit value in the buffer, filling it.
+ *
+ * @param buf Buffer to fill
+ * @param val Value to fill the buffer with
+ * @param buf_size size of the buffer
+ */
+void memset32(uint8_t* buf, uint32_t val, int buf_size)
+{
+ int i = 0;
+ while (i <= buf_size - (int)sizeof(val))
+ {
+ memcpy(buf + i, &val, sizeof(val));
+ i += sizeof(val);
+ }
+
+ memcpy(buf + i, &val, buf_size % sizeof(val));
+}
+
+struct SendIntervalBase
+{
+ virtual unsigned int getInterval() const = 0;
+};
+
+struct ConstSendInterval : public SendIntervalBase
+{
+ ConstSendInterval(unsigned int interval) : interval(interval) {}
+ unsigned int interval;
+ unsigned int getInterval() const override { return interval; }
+};
+
+struct RandSendInterval : public SendIntervalBase
+{
+ RandSendInterval(unsigned int min, unsigned int max)
+ : min(min), diff(max - min)
+ {
+ }
+ unsigned int min;
+ unsigned int diff;
+ unsigned int getInterval() const override
+ {
+ return (miosix::HardwareRng::instance().get() % diff) + min;
+ }
+};
+
+class Sender : public ActiveObject
+{
+ friend class XbeeTransceiver;
+
+public:
+ Sender(Xbee::Xbee& xbee, Logger& logger, const SendIntervalBase& interval,
+ size_t packet_size, long long expected_packet_interval)
+ : xbee(xbee), logger(logger), dr_calc(expected_packet_interval),
+ interval(interval)
+ {
+ data.packet_size = packet_size;
+ packet_counter = 0;
+ }
+
+ TxData getTxData() const { return data; }
+
+ DataRateResult getDataRate() { return dr_calc.getResult(); }
+
+protected:
+ void run() override
+ {
+ long long loop_start_ts = getTick();
+ while (!shouldStop())
+ {
+ data.time_since_last_send = getTick() - loop_start_ts;
+ loop_start_ts = getTick();
+
+ // Create packet
+ memcpy(buf, &PACKET_FIRST_INT, sizeof(uint32_t));
+ memset32(buf + sizeof(uint32_t), packet_counter++,
+ data.packet_size - sizeof(uint32_t));
+
+ long long send_start_ts = getTick();
+
+ bool result = xbee.send(buf, data.packet_size);
+
+ data.time_to_send = getTick() - send_start_ts;
+ data.timestamp = send_start_ts;
+
+ if (result)
+ {
+ ++data.tx_success_counter;
+ dr_calc.addPacket(data.packet_size);
+ }
+ else
+ {
+ ++data.tx_fail_counter;
+ }
+
+ logger.log(data);
+
+ miosix::Thread::sleepUntil(loop_start_ts + interval.getInterval());
+ }
+ }
+
+private:
+ Xbee::Xbee& xbee;
+ Logger& logger;
+ ThroughputCalculator dr_calc;
+ TxData data;
+ const SendIntervalBase& interval;
+
+ uint32_t packet_counter = 0;
+
+ uint8_t buf[Xbee::MAX_PACKET_PAYLOAD_LENGTH];
+};
+
+class Receiver : public ActiveObject
+{
+ friend class XbeeTransceiver;
+
+public:
+ Receiver(Xbee::Xbee& xbee, Logger& logger,
+ long long expected_packet_interval)
+ : xbee(xbee), logger(logger), dr_calc(expected_packet_interval)
+ {
+ }
+
+ void stop() override
+ {
+ should_stop = true;
+ xbee.wakeReceiver(true);
+ ActiveObject::stop();
+ }
+
+ RxData getRxData() const { return data; }
+
+ DataRateResult getDataRate() { return dr_calc.getResult(); }
+
+protected:
+ void run() override
+ {
+ while (!shouldStop())
+ {
+ long long start = getTick();
+
+ size_t len = xbee.receive(buf, RCV_BUF_SIZE);
+
+ data.last_packet_timestamp = getTick();
+ data.timestamp = start;
+
+ ++data.rcv_count;
+
+ if (len > sizeof(uint32_t))
+ {
+ data.pkt_size = len;
+
+ // Packet payload is a repeated sequential 32 bit number
+ uint32_t num, strt;
+ memcpy(&strt, buf, sizeof(uint32_t));
+ memcpy(&num, buf + sizeof(uint32_t), sizeof(uint32_t));
+
+ if (checkPacket(num, buf, len))
+ {
+ dr_calc.addPacket(len);
+ if (last_packet_num > 0)
+ {
+ if (num > last_packet_num)
+ data.packets_lost += num - last_packet_num - 1;
+ }
+ last_packet_num = num;
+ }
+ else
+ {
+ TRACE("[XBeeTransceiver] Wrong packet payload! num: %u, start: 0x%08X\n", num, strt);
+ ++data.rcv_wrong_payload;
+ }
+ }
+
+ logger.log(data);
+ }
+ }
+
+private:
+ bool checkPacket(uint32_t expected_num, uint8_t* packet, size_t packet_size)
+ {
+ if (memcmp(packet, &PACKET_FIRST_INT, sizeof(uint32_t)) == 0)
+ {
+ int i = sizeof(uint32_t);
+ while (i <= (int)packet_size - (int)sizeof(expected_num))
+ {
+ if (memcmp(packet + i, &expected_num, sizeof(expected_num)) !=
+ 0)
+ {
+ return false;
+ }
+ i += sizeof(expected_num);
+ }
+ memcpy(packet + i, &expected_num,
+ packet_size % sizeof(expected_num));
+ return true;
+ }
+ return false;
+ }
+
+ Xbee::Xbee& xbee;
+ Logger& logger;
+ ThroughputCalculator dr_calc;
+ RxData data{};
+
+ uint8_t buf[RCV_BUF_SIZE];
+ uint32_t last_packet_num = 0;
+};
+
+class XbeeTransceiver
+{
+public:
+ XbeeTransceiver(Xbee::Xbee& xbee, Logger& logger,
+ const SendIntervalBase& snd_interval, size_t tx_pkt_size,
+ long long expected_packet_interval)
+ : sender(new Sender(xbee, logger, snd_interval, tx_pkt_size,
+ expected_packet_interval)),
+ receiver(new Receiver(xbee, logger, expected_packet_interval)),
+ logger(logger)
+ {
+ using namespace std::placeholders;
+ xbee.setOnFrameReceivedListener(
+ bind(&XbeeTransceiver::handleApiFrame, this, _1));
+ }
+
+ ~XbeeTransceiver()
+ {
+ sender->stop();
+ receiver->stop();
+
+ delete sender;
+ delete receiver;
+ }
+
+ void disableSender() { sender_enabled = false; }
+
+ void disableReceiver() { receiver_enabled = false; }
+
+ void start()
+ {
+ if (sender_enabled)
+ {
+ sender->start();
+ }
+ if (receiver_enabled)
+ {
+ receiver->start();
+ }
+ }
+
+ void stop()
+ {
+ sender->stop();
+ receiver->stop();
+ }
+
+ void setOnFrameReceivedListener(
+ Xbee::Xbee::OnFrameReceivedListener frame_listener)
+ {
+ this->frame_listener = frame_listener;
+ }
+
+ Sender& getSender() { return *sender; }
+
+ Receiver& getReceiver() { return *receiver; }
+
+private:
+ void handleApiFrame(Xbee::APIFrame& frame)
+ {
+ logAPIFrame(frame);
+
+ if (frame_listener)
+ {
+ frame_listener(frame);
+ }
+
+ if (frame.frame_type == Xbee::FTYPE_AT_COMMAND_RESPONSE)
+ {
+ Xbee::ATCommandResponseFrame* at =
+ frame.toFrameType<Xbee::ATCommandResponseFrame>();
+
+ if (strncmp(at->getATCommand(), "DB", 2) == 0)
+ {
+ receiver->data.RSSI = -*at->getCommandDataPointer();
+ }
+
+ if (strncmp(at->getATCommand(), "ER", 2) == 0)
+ {
+ uint16_t errs;
+ memcpy(&errs, at->getCommandDataPointer(), 2);
+ receiver->data.rcv_errors = swapBytes16(errs);
+ }
+ }
+ }
+
+ void logAPIFrame(Xbee::APIFrame& frame)
+ {
+ using namespace Xbee;
+ bool logged = false;
+ switch (frame.frame_type)
+ {
+ case FTYPE_AT_COMMAND:
+ {
+ ATCommandFrameLog dest;
+ logged = ATCommandFrameLog::toFrameType(frame, &dest);
+ if (logged)
+ {
+ logger.log(dest);
+ }
+ break;
+ }
+ case FTYPE_AT_COMMAND_RESPONSE:
+ {
+ ATCommandResponseFrameLog dest;
+ logged = ATCommandResponseFrameLog::toFrameType(frame, &dest);
+ if (logged)
+ {
+ logger.log(dest);
+ }
+ break;
+ }
+ case FTYPE_MODEM_STATUS:
+ {
+ ModemStatusFrameLog dest;
+ logged = ModemStatusFrameLog::toFrameType(frame, &dest);
+ if (logged)
+ {
+ logger.log(dest);
+ }
+ break;
+ }
+ case FTYPE_TX_REQUEST:
+ {
+ TXRequestFrameLog dest;
+ logged = TXRequestFrameLog::toFrameType(frame, &dest);
+ if (logged)
+ {
+ logger.log(dest);
+ }
+ break;
+ }
+ case FTYPE_TX_STATUS:
+ {
+ TXStatusFrameLog dest;
+ logged = TXStatusFrameLog::toFrameType(frame, &dest);
+ if (logged)
+ {
+ logger.log(dest);
+ }
+ break;
+ }
+ case FTYPE_RX_PACKET_FRAME:
+ {
+ RXPacketFrameLog dest;
+ logged = RXPacketFrameLog::toFrameType(frame, &dest);
+ if (logged)
+ {
+ logger.log(dest);
+ }
+ break;
+ }
+ }
+
+ if (!logged)
+ {
+ APIFrameLog api;
+ APIFrameLog::fromAPIFrame(frame, &api);
+ logger.log(api);
+ }
+ }
+
+ bool sender_enabled = true;
+ bool receiver_enabled = true;
+
+ Sender* sender;
+ Receiver* receiver;
+ Logger& logger;
+
+ Xbee::Xbee::OnFrameReceivedListener frame_listener;
+};
\ No newline at end of file
diff --git a/src/tests/drivers/xbee/gui/ConfigScreen.h b/src/tests/drivers/xbee/gui/ConfigScreen.h
new file mode 100644
index 000000000..32140ef65
--- /dev/null
+++ b/src/tests/drivers/xbee/gui/ConfigScreen.h
@@ -0,0 +1,224 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <mxgui/display.h>
+
+#include "utils/gui/GridLayout.h"
+#include "utils/gui/TextView.h"
+#include "utils/gui/VerticalLayout.h"
+#include "utils/gui/OptionView.h"
+
+#include "../XbeeTestData.h"
+
+struct ConfigScreen
+{
+ XbeeConfig config;
+
+ enum OptSendEnableIDs : uint8_t
+ {
+ TX_ENABLED,
+ TX_DISABLED
+ };
+
+ enum OptFreqHopIDs : uint8_t
+ {
+ FH_ENABLED,
+ FH_DISABLED
+ };
+
+ enum OptDataRateIDs : uint8_t
+ {
+ DR_10K,
+ DR_80K
+ };
+
+ enum OptPacketSizeIDs : uint8_t
+ {
+ PKTSIZE_64,
+ PKTSIZE_128,
+ PKTSIZE_256
+ };
+
+ enum OptSendIntervalIDs : uint8_t
+ {
+ TXINT_CONTINUOUS,
+ TXINT_200MS,
+ TXINT_250MS,
+ TXINT_333MS,
+ TXINT_500MS,
+ TXINT_1000MS
+ };
+
+ ConfigScreen()
+ {
+ title.setFont(mxgui::miscFixedBold);
+ title.setTextColor(mxgui::black);
+ title.setBackgroundColor(mxgui::green);
+ title.setAlignment(HorizAlignment::LEFT, VertAlignment::CENTER);
+
+ tv_log_status.setFont(mxgui::miscFixedBold);
+ tv_log_status.setTextColor(mxgui::white);
+ tv_log_status.setBackgroundColor(mxgui::red);
+ tv_log_status.setAlignment(HorizAlignment::CENTER, VertAlignment::CENTER);
+
+ grid_title.setCell(&title, 0, 0);
+ grid_title.setCell(&tv_log_status, 0, 1);
+
+ grid.setCell(&btn_energy, 0);
+ grid.setCell(&btn_start, 1);
+ grid.setDrawBorder(true);
+
+ btn_start.setSelectable(true);
+ btn_start.setAlignment(HorizAlignment::CENTER, VertAlignment::CENTER);
+ btn_start.setBackgroundColor(mxgui::darkGrey);
+
+ btn_energy.setSelectable(true);
+ btn_energy.setAlignment(HorizAlignment::CENTER, VertAlignment::CENTER);
+ btn_energy.setBackgroundColor(mxgui::darkGrey);
+
+ root.addView(&grid_title, 0.6);
+ root.addView(&opt_send_enable, 1);
+ root.addView(&opt_packet_size, 1);
+
+ root.addView(&opt_send_interval, 2);
+
+ root.addView(&opt_freq_hop, 1);
+ root.addView(&opt_data_rate, 1);
+ root.addView(&grid, 1);
+
+ addOptionCallbacks();
+ }
+
+ void updateLogStatus(Logger& logger)
+ {
+ if (logger.getLogNumber() >= 0)
+ {
+ string log_name = logger.getFileName(logger.getLogNumber());
+
+ tv_log_status.setText(log_name);
+ tv_log_status.setTextColor(mxgui::black);
+ tv_log_status.setBackgroundColor(mxgui::green);
+ }
+ else
+ {
+ tv_log_status.setText("SD ERR");
+ tv_log_status.setTextColor(mxgui::white);
+ tv_log_status.setBackgroundColor(mxgui::red);
+ }
+ }
+
+ VerticalLayout root{10};
+
+ TextView btn_start{"Start"};
+ TextView btn_energy{"Energy scan"};
+
+private:
+ void addOptionCallbacks()
+ {
+ opt_send_enable.addOnOptionChosenListener(
+ [&](unsigned int id) { config.tx_enabled = id == TX_ENABLED; });
+
+ opt_send_interval.addOnOptionChosenListener([&](unsigned int id) {
+ switch (id)
+ {
+ case TXINT_CONTINUOUS:
+ config.send_interval = 0;
+ break;
+ case TXINT_200MS:
+ config.send_interval = 200;
+ break;
+ case TXINT_250MS:
+ config.send_interval = 250;
+ break;
+ case TXINT_333MS:
+ config.send_interval = 333;
+ break;
+ case TXINT_500MS:
+ config.send_interval = 500;
+ break;
+ case TXINT_1000MS:
+ config.send_interval = 1000;
+ break;
+ }
+ });
+
+ opt_packet_size.addOnOptionChosenListener([&](unsigned int id) {
+ switch (id)
+ {
+ case PKTSIZE_64:
+ config.packet_size = 64;
+ break;
+ case PKTSIZE_128:
+ config.packet_size = 128;
+ break;
+ case PKTSIZE_256:
+ config.packet_size = 256;
+ break;
+ }
+ });
+
+ opt_freq_hop.addOnOptionChosenListener(
+ [&](unsigned int id) { config.freq_hop = id == FH_ENABLED; });
+
+ opt_data_rate.addOnOptionChosenListener(
+ [&](unsigned int id) { config.data_rate_80k = id == DR_80K; });
+ }
+
+ OptionView opt_send_enable{
+ "Transmission",
+ {{TX_ENABLED, "Enabled"}, {TX_DISABLED, "Disabled"}},
+ TX_DISABLED,
+ 2};
+
+ OptionView opt_packet_size{
+ "TX Packet Size",
+ {{PKTSIZE_64, "64 B"}, {PKTSIZE_128, "128 B"}, {PKTSIZE_256, "256 B"}},
+ PKTSIZE_256,
+ 3};
+
+ OptionView opt_send_interval{"Send Interval",
+ {{TXINT_CONTINUOUS, "Cont"},
+ {TXINT_200MS, "200 ms"},
+ {TXINT_250MS, "250 ms"},
+ {TXINT_333MS, "333 ms"},
+ {TXINT_500MS, "500 ms"},
+ {TXINT_1000MS, "1 sec"}},
+ TXINT_CONTINUOUS,
+ 4};
+
+ OptionView opt_freq_hop{
+ "Frequency hopping",
+ {{FH_ENABLED, "Enabled"}, {FH_DISABLED, "Disabled"}},
+ FH_ENABLED,
+ 2};
+ OptionView opt_data_rate{
+ "Xbee datarate", {{DR_10K, "10 kbps"}, {DR_80K, "80 kbps"}}, DR_10K, 2};
+
+ GridLayout grid{1, 2};
+ GridLayout grid_title{1,2};
+ TextView title{"Xbee Setup"};
+ TextView tv_log_status{"SD ERR!"};
+};
\ No newline at end of file
diff --git a/src/tests/drivers/xbee/gui/EndScreen.h b/src/tests/drivers/xbee/gui/EndScreen.h
new file mode 100644
index 000000000..4c19f211a
--- /dev/null
+++ b/src/tests/drivers/xbee/gui/EndScreen.h
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <mxgui/display.h>
+
+#include "utils/gui/GridLayout.h"
+#include "utils/gui/TextView.h"
+#include "utils/gui/VerticalLayout.h"
+#include "utils/gui/OptionView.h"
+
+
+
+struct EndScreen
+{
+ EndScreen()
+ {
+ tv_title.setAlignment(HorizAlignment::CENTER, VertAlignment::CENTER);
+ tv_f.setAlignment(HorizAlignment::CENTER, VertAlignment::CENTER);
+ tv_reset.setAlignment(HorizAlignment::CENTER, VertAlignment::CENTER);
+
+ tv_f.setSelectable(true);
+ tv_reset.setSelectable(true);
+
+ root.addView(&tv_title, 1);
+ root.addView(&tv_f, 1);
+ root.addView(&tv_reset, 1);
+ }
+
+ VerticalLayout root;
+
+ TextView tv_f{"Press F to pay respects"};
+ TextView tv_reset{"Or RESET to start again"};
+private:
+ TextView tv_title{"You killed the program, you maniac!"};
+};
\ No newline at end of file
diff --git a/src/tests/drivers/xbee/gui/EnergyScanScreen.h b/src/tests/drivers/xbee/gui/EnergyScanScreen.h
new file mode 100644
index 000000000..a463f9067
--- /dev/null
+++ b/src/tests/drivers/xbee/gui/EnergyScanScreen.h
@@ -0,0 +1,232 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <mxgui/display.h>
+
+#include <array>
+#include <cstdint>
+#include <limits>
+#include <string>
+#include <utility>
+
+#include "math/Stats.h"
+#include "utils/gui/GridLayout.h"
+#include "utils/gui/TextView.h"
+#include "utils/gui/VerticalLayout.h"
+
+using std::array;
+using std::to_string;
+
+struct EnergyScanScreen
+{
+ static constexpr unsigned int NUM_CHANNELS = 30;
+ static constexpr unsigned int NUM_CHANNEL_ROWS = (NUM_CHANNELS + 1) / 2;
+ static constexpr unsigned int NUM_COLS = 8;
+
+ static constexpr float COLOR_PERCENTILE = 0.15f;
+ EnergyScanScreen()
+ {
+ tv_title.setFont(mxgui::miscFixedBold);
+ tv_title.setTextColor(mxgui::black);
+ tv_title.setBackgroundColor(mxgui::green);
+ tv_title.setAlignment(HorizAlignment::CENTER, VertAlignment::CENTER);
+
+ tv_log_status.setFont(mxgui::miscFixedBold);
+ tv_log_status.setTextColor(mxgui::white);
+ tv_log_status.setBackgroundColor(mxgui::red);
+ tv_log_status.setAlignment(HorizAlignment::CENTER,
+ VertAlignment::CENTER);
+
+ grid_title.setCell(&tv_title, 0, 0);
+ grid_title.setCell(&tv_log_status, 0, 1);
+
+ for (unsigned int i = 0; i < NUM_COLS; i++)
+ {
+ col_titles[i] = new TextView(titles[i % (NUM_COLS / 2)]);
+ col_titles[i]->setTextColor(mxgui::blue);
+
+ grid_channels.setCell(col_titles[i], 0, i);
+ }
+
+ mxgui::Color pink(0xFC59);
+ for (unsigned int i = 0; i < NUM_CHANNELS; ++i)
+ {
+ char buf[3];
+ snprintf(buf, 3, "%02d", i);
+
+ col_names[i] = new TextView(buf);
+ col_names[i]->setTextColor(pink);
+
+ col_current[i] = new TextView("-40");
+ col_min[i] = new TextView("-40");
+ col_max[i] = new TextView("-40");
+
+ grid_channels.setCell(col_names[i], (i % NUM_CHANNEL_ROWS) + 1,
+ (i / NUM_CHANNEL_ROWS) * 4 + 0);
+ grid_channels.setCell(col_current[i], (i % NUM_CHANNEL_ROWS) + 1,
+ (i / NUM_CHANNEL_ROWS) * 4 + 1);
+ grid_channels.setCell(col_min[i], (i % NUM_CHANNEL_ROWS) + 1,
+ (i / NUM_CHANNEL_ROWS) * 4 + 2);
+ grid_channels.setCell(col_max[i], (i % NUM_CHANNEL_ROWS) + 1,
+ (i / NUM_CHANNEL_ROWS) * 4 + 3);
+ }
+
+ btn_mark.setSelectable(true);
+ btn_mark.setAlignment(HorizAlignment::CENTER, VertAlignment::CENTER);
+ btn_mark.setBackgroundColor(mxgui::darkGrey);
+
+ btn_stop.setSelectable(true);
+ btn_stop.setAlignment(HorizAlignment::CENTER, VertAlignment::CENTER);
+ btn_stop.setBackgroundColor(mxgui::darkGrey);
+
+ btn_reset.setSelectable(true);
+ btn_reset.setAlignment(HorizAlignment::CENTER, VertAlignment::CENTER);
+ btn_reset.setBackgroundColor(mxgui::darkGrey);
+
+ grid_buttons.setCell(&btn_mark, 0);
+ grid_buttons.setCell(&btn_reset, 1);
+ grid_buttons.setCell(&btn_stop, 2);
+ grid_buttons.setDrawBorder(true);
+
+ root.addView(&grid_title, 0.8);
+ root.addView(&grid_channels, 10);
+ root.addView(&grid_buttons, 1);
+ }
+
+ ~EnergyScanScreen()
+ {
+ for (unsigned int i = 0; i < NUM_COLS; i++)
+ {
+ delete col_titles[i];
+ }
+
+ for (unsigned int i = 0; i < NUM_CHANNELS; ++i)
+ {
+ delete col_names[i];
+ delete col_current[i];
+ delete col_min[i];
+ delete col_max[i];
+ }
+ }
+
+ void updateScan(array<int, NUM_CHANNELS> scan)
+ {
+ for (unsigned int i = 0; i < NUM_CHANNELS; i++)
+ {
+ ch_stats[i].add(scan[i]);
+
+ StatsResult r = ch_stats[i].getStats();
+
+ abs_max = std::max(r.maxValue, abs_max);
+ abs_min = std::min(r.minValue, abs_min);
+ }
+
+ for (unsigned int i = 0; i < NUM_CHANNELS; i++)
+ {
+ StatsResult r = ch_stats[i].getStats();
+
+ col_min[i]->setText(to_string((int)r.minValue));
+ setColor(col_min[i], r.minValue);
+
+ col_current[i]->setText(to_string(scan[i]));
+ setColor(col_current[i], scan[i]);
+
+ col_max[i]->setText(to_string((int)r.maxValue));
+ setColor(col_max[i], r.maxValue);
+ }
+ }
+
+ void resetStats()
+ {
+ for (unsigned int i = 0; i < NUM_CHANNELS; i++)
+ {
+ ch_stats[i].reset();
+ }
+
+ abs_max = std::numeric_limits<float>::lowest();
+ abs_min = std::numeric_limits<float>::max();
+ }
+
+ void updateLogStatus(Logger& logger)
+ {
+ if (logger.getLogNumber() >= 0)
+ {
+ string log_name = logger.getFileName(logger.getLogNumber());
+
+ tv_log_status.setText(log_name);
+ tv_log_status.setTextColor(mxgui::black);
+ tv_log_status.setBackgroundColor(mxgui::green);
+ }
+ else
+ {
+ tv_log_status.setText("SD ERR");
+ tv_log_status.setTextColor(mxgui::white);
+ tv_log_status.setBackgroundColor(mxgui::red);
+ }
+ }
+
+ VerticalLayout root{5};
+
+ TextView btn_mark{"Mark Log (1)"};
+ TextView btn_stop{"Stop"};
+ TextView btn_reset{"Reset"};
+
+private:
+ void setColor(TextView* tv, float val)
+ {
+ float delta = abs_max - abs_min;
+ if (val >= abs_max - delta * COLOR_PERCENTILE)
+ {
+ tv->setTextColor(mxgui::green);
+ }
+ else if (val <= abs_min + delta * COLOR_PERCENTILE)
+ {
+ tv->setTextColor(0xFDC0); // Light orange
+ }
+ else
+ {
+ tv->setTextColor(mxgui::white);
+ }
+ }
+ float abs_max = std::numeric_limits<float>::lowest();
+ float abs_min = std::numeric_limits<float>::max();
+
+ GridLayout grid_channels{NUM_CHANNEL_ROWS + 1, NUM_COLS};
+ GridLayout grid_title{1, 2};
+ GridLayout grid_buttons{1, 3};
+
+ TextView tv_log_status{"SD ERR"};
+ TextView tv_title{"Energy Scan"};
+
+ array<std::string, NUM_COLS / 2> titles = {"CH", "Curr", "Min", "Max"};
+ array<TextView*, NUM_COLS> col_titles;
+
+ array<TextView*, NUM_CHANNELS> col_names;
+ array<TextView*, NUM_CHANNELS> col_current;
+ array<TextView*, NUM_CHANNELS> col_min;
+ array<TextView*, NUM_CHANNELS> col_max;
+
+ array<Stats, NUM_CHANNELS> ch_stats;
+};
\ No newline at end of file
diff --git a/src/tests/drivers/xbee/gui/RespectScreen.h b/src/tests/drivers/xbee/gui/RespectScreen.h
new file mode 100644
index 000000000..2dbc1d6ef
--- /dev/null
+++ b/src/tests/drivers/xbee/gui/RespectScreen.h
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <mxgui/display.h>
+
+#include "res/respect.h"
+#include "utils/gui/ImageView.h"
+
+struct RespectScreen
+{
+ RespectScreen()
+ {
+
+ }
+
+ ImageView root{&respect};
+};
\ No newline at end of file
diff --git a/src/tests/drivers/xbee/gui/StatusScreen.h b/src/tests/drivers/xbee/gui/StatusScreen.h
new file mode 100644
index 000000000..b9661a404
--- /dev/null
+++ b/src/tests/drivers/xbee/gui/StatusScreen.h
@@ -0,0 +1,334 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <mxgui/display.h>
+
+#include <cstdint>
+#include <string>
+#include <cstring>
+
+#include "utils/testutils/ThroughputCalculator.h"
+#include "../XbeeTestData.h"
+#include "logger/Logger.h"
+
+#include "utils/gui/GridLayout.h"
+#include "utils/gui/OptionView.h"
+#include "utils/gui/TextView.h"
+#include "utils/gui/VerticalLayout.h"
+
+using std::to_string;
+
+/**
+ * @brief Converts tick in milliseconds to the HH:MM:SS format
+ */
+std::string tickToHMS(long long tick)
+{
+ char buf[15];
+
+ int h = tick / (1000 * 3600);
+ tick -= h * (1000 * 3600);
+ int m = tick / (1000 * 60);
+ tick -= m * (1000 * 60);
+ int s = tick/1000;
+
+ snprintf(buf, 15, "%02d:%02d:%02d", h, m, s);
+
+ return string(buf);
+}
+
+struct StatusScreen
+{
+ XbeeConfig config;
+
+ StatusScreen()
+ {
+ title.setFont(mxgui::miscFixedBold);
+ title.setTextColor(mxgui::black);
+ title.setBackgroundColor(mxgui::green);
+ title.setAlignment(HorizAlignment::CENTER, VertAlignment::CENTER);
+
+ tv_log_status.setFont(mxgui::miscFixedBold);
+ tv_log_status.setTextColor(mxgui::white);
+ tv_log_status.setBackgroundColor(mxgui::red);
+ tv_log_status.setAlignment(HorizAlignment::CENTER,
+ VertAlignment::CENTER);
+
+ grid_title.setCell(&title, 0, 0);
+ grid_title.setCell(&tv_log_status, 0, 1);
+
+ grid_config.setCell(&tv_cfg_txt_tx_enabled, 0, 0);
+ grid_config.setCell(&tv_cfg_tx_enabled, 0, 1);
+
+ grid_config.setCell(&tv_cfg_txt_pkt_size, 0, 2);
+ grid_config.setCell(&tv_cfg_pkt_size, 0, 3);
+
+ grid_config.setCell(&tv_cfg_txt_snd_interval, 1, 0);
+ grid_config.setCell(&tv_cfg_snd_interval, 1, 1);
+
+ grid_config.setCell(&tv_cfg_txt_freq_hop, 1, 2);
+ grid_config.setCell(&tv_cfg_freq_hop, 1, 3);
+
+ grid_config.setCell(&tv_cfg_txt_data_rate, 2, 0);
+ grid_config.setCell(&tv_cfg_data_rate, 2, 1);
+
+ tv_log_title.setTextColor(mxgui::blue);
+
+ grid_log_status.setCell(&tv_log_title, 0, 0);
+
+ grid_log_status.setCell(&tv_log_txt_buf_written, 1, 0);
+ grid_log_status.setCell(&tv_log_buf_written, 1, 1);
+
+ grid_log_status.setCell(&tv_log_txt_buf_ttw, 1, 2);
+ grid_log_status.setCell(&tv_log_buf_ttw, 1, 3);
+
+ grid_log_status.setCell(&tv_log_txt_buf_dropped, 2, 0);
+ grid_log_status.setCell(&tv_log_buf_dropped, 2, 1);
+
+ grid_log_status.setCell(&tv_log_txt_buf_failed, 2, 2);
+ grid_log_status.setCell(&tv_log_buf_failed, 2, 3);
+
+ tv_tx_title.setTextColor(mxgui::blue);
+
+ grid_data.setCell(&tv_tx_title, 0, 0);
+
+ grid_data.setCell(&tv_tx_txt_num_pkt, 1, 0);
+ grid_data.setCell(&tv_tx_num_pkt, 1, 1);
+
+ grid_data.setCell(&tv_tx_txt_num_fail, 2, 0);
+ grid_data.setCell(&tv_tx_num_fail, 2, 1);
+
+ grid_data.setCell(&tv_tx_txt_pps, 3, 0);
+ grid_data.setCell(&tv_tx_pps, 3, 1);
+
+ grid_data.setCell(&tv_tx_txt_TTS, 4, 0);
+ grid_data.setCell(&tv_tx_tts, 4, 1);
+
+ grid_data.setCell(&tv_tx_txt_last_status, 5, 0);
+ grid_data.setCell(&tv_tx_last_status, 5, 1);
+
+ grid_data.setCell(&tv_tx_txt_last_err, 6, 0);
+ grid_data.setCell(&tv_tx_last_err, 6, 1);
+
+ tv_rx_title.setTextColor(mxgui::blue);
+ grid_data.setCell(&tv_rx_title, 0, 2);
+
+ grid_data.setCell(&tv_rx_txt_num_pkt, 1, 2);
+ grid_data.setCell(&tv_rx_num_pkt, 1, 3);
+
+ grid_data.setCell(&tv_rx_txt_num_fail, 2, 2);
+ grid_data.setCell(&tv_rx_num_fail, 2, 3);
+
+ grid_data.setCell(&tv_rx_txt_lost, 3, 2);
+ grid_data.setCell(&tv_rx_lost, 3, 3);
+
+ grid_data.setCell(&tv_rx_txt_RSSI, 4, 2);
+ grid_data.setCell(&tv_rx_RSSI, 4, 3);
+
+ grid_data.setCell(&tv_rx_txt_data_rate, 5, 2);
+ grid_data.setCell(&tv_rx_data_rate, 5, 3);
+
+ grid_data.setCell(&tv_rx_txt_pps, 6, 2);
+ grid_data.setCell(&tv_rx_pps, 6, 3);
+
+ grid_data.setCell(&tv_rx_txt_time_since_last_rx, 7, 2);
+ grid_data.setCell(&tv_rx_time_since_last_rx, 7, 3);
+
+ btn_mark.setSelectable(true);
+ btn_mark.setAlignment(HorizAlignment::CENTER,
+ VertAlignment::CENTER);
+ btn_mark.setBackgroundColor(mxgui::darkGrey);
+
+ btn_stop.setSelectable(true);
+ btn_stop.setAlignment(HorizAlignment::CENTER, VertAlignment::CENTER);
+ btn_stop.setBackgroundColor(mxgui::darkGrey);
+
+ grid_buttons.setCell(&btn_mark, 0);
+ grid_buttons.setCell(&btn_stop, 1);
+ grid_buttons.setDrawBorder(true);
+
+ root.addView(&grid_title, 0.8);
+ root.addView(&grid_config, 1.5);
+ root.addView(&grid_log_status, 1.5);
+ root.addView(&grid_data, 5);
+ root.addView(&grid_buttons, 1);
+ }
+
+ void updateConfig(XbeeConfig cfg)
+ {
+ // Update GUI with selected config values
+ tv_cfg_tx_enabled.setText(cfg.tx_enabled ? "Enabled" : "Disabled");
+ tv_cfg_pkt_size.setText(std::to_string(cfg.packet_size));
+ tv_cfg_snd_interval.setText(cfg.send_interval == 0
+ ? "Cont"
+ : std::to_string(cfg.send_interval));
+
+ tv_cfg_freq_hop.setText(cfg.freq_hop ? "Enabled" : "Disabled");
+ tv_cfg_data_rate.setText(cfg.data_rate_80k ? "80 kbps" : "10 kbps");
+ }
+
+ void updateLogStatus(Logger& logger)
+ {
+ LogStats stats = logger.getLogStats();
+
+ if (logger.getLogNumber() >= 0)
+ {
+ string log_name = logger.getFileName(logger.getLogNumber());
+
+ tv_log_status.setText(log_name);
+ tv_log_status.setTextColor(mxgui::black);
+ tv_log_status.setBackgroundColor(mxgui::green);
+ }
+ else
+ {
+ tv_log_status.setText("SD ERR");
+ tv_log_status.setTextColor(mxgui::white);
+ tv_log_status.setBackgroundColor(mxgui::red);
+ }
+
+ tv_log_buf_dropped.setText(to_string(stats.statDroppedSamples));
+
+ if (stats.statDroppedSamples > 0)
+ {
+ tv_log_buf_dropped.setBackgroundColor(mxgui::red);
+ }
+
+ tv_log_buf_failed.setText(to_string(stats.statWriteFailed) + " (" +
+ to_string(stats.statWriteError) + ")");
+
+ if (stats.statWriteError != 0)
+ {
+ tv_log_buf_failed.setBackgroundColor(mxgui::red);
+ }
+
+ tv_log_buf_written.setText(to_string(stats.statBufferWritten));
+ tv_log_buf_ttw.setText(to_string(stats.statWriteTime) + " ms");
+ }
+
+ void updateXbeeStatus(DataRateResult res_rcv, DataRateResult res_snd,
+ TxData txd, RxData rxd, Xbee::XbeeStatus xbee_status)
+ {
+ char str_buf[30];
+
+ tv_tx_num_pkt.setText(
+ to_string(txd.tx_success_counter + txd.tx_fail_counter));
+ tv_tx_num_fail.setText(to_string(txd.tx_fail_counter));
+
+ snprintf(str_buf, 30, "%.1f pkt/s", res_snd.packets_per_second);
+
+ tv_tx_pps.setText(str_buf);
+ tv_tx_tts.setText(to_string(txd.time_to_send) + " ms");
+
+ tv_tx_last_status.setText(to_string(xbee_status.last_tx_status));
+ tv_tx_last_err.setText(to_string(xbee_status.last_tx_status_error));
+
+ tv_rx_num_pkt.setText(to_string(rxd.rcv_count));
+ tv_rx_num_fail.setText(to_string(rxd.rcv_errors));
+ // tv_rx_num_fail.setText(to_string(int_counter) + " " +
+ // to_string(GpioATTN::value()));
+ tv_rx_lost.setText(to_string(rxd.packets_lost));
+
+ tv_rx_RSSI.setText(to_string(rxd.RSSI) + " dB");
+
+ snprintf(str_buf, 30, "%.0f B/s", res_rcv.data_rate);
+ tv_rx_data_rate.setText(str_buf);
+
+ snprintf(str_buf, 30, "%.1f pkt/s", res_rcv.packets_per_second);
+ tv_rx_pps.setText(str_buf);
+
+ tv_rx_time_since_last_rx.setText(tickToHMS(miosix::getTick() - rxd.last_packet_timestamp));
+ }
+
+ VerticalLayout root{10};
+
+ TextView tv_cfg_tx_enabled{"Disabled"};
+ TextView tv_cfg_pkt_size{"256 B"};
+ TextView tv_cfg_snd_interval{"Cont"};
+ TextView tv_cfg_freq_hop{"Enabled"};
+ TextView tv_cfg_data_rate{"10 kbps"};
+
+ TextView tv_log_status{"SD ERR"};
+
+ TextView tv_log_buf_dropped{"0"};
+ TextView tv_log_buf_failed{"0 (0)"};
+ TextView tv_log_buf_written{"0"};
+ TextView tv_log_buf_ttw{"0 ms"};
+
+ TextView tv_tx_num_pkt{"0"};
+ TextView tv_tx_num_fail{"0"};
+ TextView tv_tx_pps{"0 pkt/s"};
+ TextView tv_tx_tts{"- ms"};
+ TextView tv_tx_last_status{"0"};
+ TextView tv_tx_last_err{"0"};
+
+ TextView tv_rx_num_pkt{"0"};
+ TextView tv_rx_num_fail{"0"};
+ TextView tv_rx_lost{"0"};
+ TextView tv_rx_RSSI{"-40 dB"};
+ TextView tv_rx_data_rate{"0 B/s"};
+ TextView tv_rx_pps{"0 pkt/s"};
+ TextView tv_rx_time_since_last_rx{"00:00:00"};
+
+ TextView btn_mark{"Mark Log (1)"};
+ TextView btn_stop{"Stop"};
+
+private:
+ TextView title{"Xbee Status"};
+ GridLayout grid_title{1, 2};
+ GridLayout grid_config{3, 4};
+ GridLayout grid_buttons{1, 2};
+ GridLayout grid_log_status{3, 4};
+ GridLayout grid_data{8, 4};
+
+ TextView tv_cfg_txt_tx_enabled{"TX"};
+ TextView tv_cfg_txt_pkt_size{"Pkt size"};
+ TextView tv_cfg_txt_snd_interval{"Interv"};
+ TextView tv_cfg_txt_freq_hop{"Freq hop"};
+ TextView tv_cfg_txt_data_rate{"Data rate"};
+
+ TextView tv_log_title{"LOG"};
+ TextView tv_log_txt_buf_dropped{"Buf drops"};
+ TextView tv_log_txt_buf_failed{"Wrt fails"};
+ TextView tv_log_txt_buf_written{"Wrt succ"};
+ TextView tv_log_txt_buf_ttw{"TTW"};
+
+ TextView tv_tx_title{"TX"};
+
+ TextView tv_tx_txt_num_pkt{"Sent"};
+ TextView tv_tx_txt_num_fail{"Fails"};
+ TextView tv_tx_txt_pps{"PPS"};
+ TextView tv_tx_txt_TTS{"TTS"};
+ TextView tv_tx_txt_last_status{"Status"};
+ TextView tv_tx_txt_last_err{"Last err"};
+
+ TextView tv_rx_title{"RX"};
+
+ TextView tv_rx_txt_num_pkt{"Recv"};
+ TextView tv_rx_txt_num_fail{"Fails"};
+ TextView tv_rx_txt_lost{"Lost"};
+ TextView tv_rx_txt_RSSI{"RSSI"};
+ TextView tv_rx_txt_data_rate{"DR"};
+ TextView tv_rx_txt_pps{"PPS"};
+ TextView tv_rx_txt_time_since_last_rx{"No RX dt"};
+};
\ No newline at end of file
diff --git a/src/tests/drivers/xbee/gui/XbeeGui.h b/src/tests/drivers/xbee/gui/XbeeGui.h
new file mode 100644
index 000000000..5e5062f2a
--- /dev/null
+++ b/src/tests/drivers/xbee/gui/XbeeGui.h
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <mxgui/display.h>
+
+#include <cstdint>
+#include <functional>
+
+#include "ConfigScreen.h"
+#include "StatusScreen.h"
+#include "EnergyScanScreen.h"
+#include "EndScreen.h"
+#include "RespectScreen.h"
+#include "utils/gui/ScreenManager.h"
+
+class XbeeGUI
+{
+public:
+ enum Screens : uint8_t
+ {
+ SCREEN_CONFIG,
+ SCREEN_STATUS,
+ SCREEN_ENERGYSCAN,
+ SCREEN_END,
+ SCREEN_RESPECT
+ };
+
+ XbeeGUI()
+ : screen_manager(mxgui::DisplayManager::instance(), 4)
+ {
+ screen_manager.addScreen(SCREEN_CONFIG, &screen_config.root);
+ screen_manager.addScreen(SCREEN_STATUS, &screen_status.root);
+ screen_manager.addScreen(SCREEN_ENERGYSCAN, &screen_energy.root);
+ screen_manager.addScreen(SCREEN_END, &screen_end.root);
+ screen_manager.addScreen(SCREEN_RESPECT, &screen_respect.root);
+
+ screen_manager.start();
+ }
+
+ ~XbeeGUI()
+ {
+ screen_manager.stop();
+ }
+
+ ScreenManager screen_manager;
+
+ ConfigScreen screen_config{};
+ StatusScreen screen_status{};
+ EnergyScanScreen screen_energy{};
+ EndScreen screen_end{};
+ RespectScreen screen_respect{};
+};
\ No newline at end of file
diff --git a/src/tests/drivers/xbee/gui/res/respect.cpp b/src/tests/drivers/xbee/gui/res/respect.cpp
new file mode 100644
index 000000000..90e91dd6a
--- /dev/null
+++ b/src/tests/drivers/xbee/gui/res/respect.cpp
@@ -0,0 +1,9614 @@
+
+//This file has been automatcally generated by pngconverter utility
+//Please do not edit
+#include "respect.h"
+
+using namespace mxgui;
+
+static const short int height=320;
+static const short int width =240;
+
+static const unsigned short pixelData[]={
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,2145,6371,19049,
+ 29614,35921,38066,38034,35953,29614,21130,10597,
+ 2145,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,10597,33808,50712,61277,65503,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 54970,40147,21130,4258,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,10597,42260,63422,65535,65535,65535,65535,
+ 65503,65503,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,57051,31727,6371,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 29582,61277,65503,65535,65503,59164,42260,31727,
+ 23243,19017,16936,21162,27501,35953,48631,61277,
+ 63422,65535,65535,65535,65535,61277,35921,4258,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,4226,40179,
+ 65535,65535,61277,38034,14823,32,0,0,
+ 0,0,0,32,0,0,32,2145,
+ 19017,33840,57083,65535,65535,65535,65535,52857,
+ 14823,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,4226,44405,65503,
+ 63390,38034,6371,0,32,4226,14823,23275,
+ 27469,25388,21162,16904,10565,4258,32,32,
+ 0,0,2145,25388,57051,65535,65535,65535,
+ 61309,19049,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2113,42260,65503,57083,
+ 19017,2113,32,4258,33840,57083,65503,65503,
+ 65535,65535,65503,65503,63422,61277,50744,38066,
+ 23243,6339,32,32,4258,38066,65535,65535,
+ 65535,59196,10565,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,31727,65503,52857,12678,
+ 0,0,14791,52857,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65503,63390,44373,19049,2113,32,27501,63390,
+ 65535,65535,50744,2113,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,14823,63390,57051,8452,0,
+ 0,12710,59164,65535,65535,63390,63390,63422,
+ 65535,65535,65535,65535,65535,65503,65535,65535,
+ 65535,65503,65503,65503,48599,10565,0,27501,
+ 65503,65535,65503,25388,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,2145,54938,63422,16936,0,0,
+ 4258,54938,65535,63390,31695,12678,6371,12678,
+ 14823,19017,23243,27469,29582,31727,35953,40179,
+ 52825,63390,65535,65535,65503,57083,12678,32,
+ 40147,65535,65535,52857,2113,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,31727,65535,31695,32,0,0,
+ 35953,65535,63390,21162,0,0,0,0,
+ 0,0,0,32,0,0,0,32,
+ 0,12678,38066,63422,65535,65535,52825,4258,
+ 6371,59164,65535,65503,19049,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,10565,61309,54938,4226,0,0,14791,
+ 63422,65503,31695,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,19017,59196,65535,65535,27469,
+ 0,29582,65535,65535,42260,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,0,0,
+ 32,42292,65535,23243,0,0,2145,48599,
+ 65535,44405,2113,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,16904,61309,65535,52857,
+ 2113,6371,61277,65535,57051,2145,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 12710,63390,52857,2113,32,0,19049,63422,
+ 57083,8484,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,27501,65535,63422,
+ 12710,32,46486,65535,63422,12710,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,0,32,
+ 42260,65535,29582,0,0,2113,50712,65535,
+ 27501,0,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,50744,65503,
+ 33808,0,21130,65535,65535,25356,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,6371,
+ 65503,61277,8452,0,0,16936,65535,54970,
+ 4226,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,21130,65503,
+ 50712,0,6371,61309,65535,40179,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,0,33808,
+ 65535,44373,32,0,0,44405,65503,29582,
+ 32,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,4226,57083,
+ 61309,4258,32,52857,65535,54970,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,2145,54970,
+ 65535,23275,0,0,12678,63422,61277,8452,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,42292,
+ 65535,16904,0,42292,65535,59196,4258,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,16936,65535,
+ 61277,6339,0,0,35953,65535,52825,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,0,27469,
+ 65535,27469,32,29582,65535,63422,12678,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,42260,65535,
+ 50712,0,0,6339,57083,65535,38034,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,14823,
+ 65535,31727,0,23243,65535,65535,19017,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,4258,59164,65535,
+ 33808,0,32,19049,65503,65535,29582,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,32,0,8484,
+ 63422,35953,0,16936,65535,65535,25388,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,16936,65503,65503,
+ 14823,0,0,40179,65535,65535,25388,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,4258,
+ 57083,35921,0,12710,65535,65535,31695,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,35921,65535,59196,
+ 4226,0,4226,57083,65535,65535,27469,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,2113,
+ 52857,31695,0,12678,65535,65535,38066,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,52857,65535,48599,
+ 0,0,12710,65535,65535,65535,33808,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,8452,
+ 61309,27501,0,10565,65535,65535,44373,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,8484,63390,65535,31727,
+ 0,32,31727,65535,65535,65535,44373,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,10565,
+ 63390,21162,0,12678,65535,65503,44405,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,23275,65503,65503,19017,
+ 0,32,48631,65535,65535,65535,50712,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,10565,
+ 63390,16936,0,14791,65535,65535,44373,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,32,40147,65535,63390,8452,
+ 0,4226,59196,65535,65535,65535,57083,2113,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,10565,
+ 61309,10565,0,16904,65535,65535,42292,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,2113,52857,65535,57083,2113,
+ 32,16904,65535,65535,65535,65535,59196,2145,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,10565,
+ 61309,6339,0,19017,65503,65535,42260,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,6339,63390,65535,46486,32,
+ 0,29582,65535,65535,65535,65535,61309,2145,
+ 0,0,0,0,0,32,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,0,19049,
+ 59196,2145,0,25356,65535,65535,38066,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,14823,65503,65535,35953,0,
+ 0,42260,65535,65535,65535,65535,61309,6371,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,29614,
+ 57051,2113,0,31695,65535,65535,33840,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,29582,65535,65535,25356,0,
+ 2113,54938,65535,65535,65503,65535,63390,8484,
+ 32,0,2113,4226,10597,19017,27469,31695,
+ 31695,25356,14823,8484,2113,32,0,32,
+ 0,0,0,0,0,0,2113,46486,
+ 50744,32,0,40147,65535,65535,27469,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,40147,65535,65503,16936,0,
+ 4258,61309,65535,65535,65535,65535,65503,23243,
+ 2145,16904,40147,59164,65503,65535,65535,65535,
+ 65535,65535,65503,63422,52857,31727,8484,2145,
+ 0,0,0,0,32,0,4258,57083,
+ 48599,0,32,46486,65535,65503,21162,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,48599,65535,63390,10565,0,
+ 10597,65503,65535,65535,65535,65535,65503,63422,
+ 57051,65503,65535,65535,65535,65535,65503,65535,
+ 65535,65535,65535,65535,65535,65535,63390,44373,
+ 16904,32,0,0,0,32,23275,65535,
+ 42292,32,2113,52825,65535,65503,16936,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,2145,54938,65535,61309,8452,0,
+ 21162,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,59164,48631,35921,
+ 29582,29582,38066,50712,59164,65535,65535,65535,
+ 63422,44405,12678,32,0,4258,54938,65535,
+ 35953,0,4226,54970,65535,63390,12678,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,4258,61277,65535,59196,4258,0,
+ 31727,63390,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,54970,23275,4258,0,32,
+ 0,0,32,32,6371,27469,54938,65535,
+ 65535,65503,61277,31727,10565,42260,65535,65535,
+ 29582,0,6371,61277,65535,59196,4258,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,8452,63390,65535,59164,2145,0,
+ 21130,48599,59164,63422,65535,65535,65535,65535,
+ 65535,65503,65535,52825,40179,33808,23275,14791,
+ 10565,6339,2113,0,0,0,4226,35953,
+ 65535,65535,65535,65535,63390,65535,65535,65535,
+ 23275,0,8484,63422,65535,57083,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,10565,65503,65535,52825,32,0,
+ 32,32,6339,12710,31695,46486,59196,63422,
+ 65503,65535,65535,65535,65535,65535,65535,65535,
+ 63422,59196,54970,48599,40147,29582,19017,14823,
+ 59196,65535,65535,65503,65535,65535,65535,65503,
+ 16936,0,14791,65535,65535,46486,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,12678,65535,65535,50744,32,0,
+ 32,0,0,32,0,0,4226,12678,
+ 25356,38066,50744,59196,65503,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65503,
+ 65503,65535,65535,65535,65535,65535,65535,63390,
+ 10565,32,23275,65535,65535,38066,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,14823,65535,65535,48631,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,4258,12710,23275,38066,48631,
+ 57083,63390,65503,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,63390,42292,
+ 32,0,31727,65535,65535,25388,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,16936,65535,65535,46518,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 4226,8484,16936,29582,42292,52857,61309,63422,
+ 65503,65503,65535,65535,65503,59164,31727,2145,
+ 0,32,38066,65535,65535,16936,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,16936,65535,65535,46486,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,4258,12678,
+ 21130,27501,29614,25388,16904,2145,0,2113,
+ 0,0,48599,65535,63390,8484,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,16936,65535,65535,44373,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,32,0,
+ 0,32,50712,65535,61277,6371,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,16936,65535,65535,42260,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,0,
+ 0,2145,52857,65535,57083,2145,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,16936,65535,65535,40179,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,0,32,
+ 0,0,0,32,0,0,0,0,
+ 0,4226,57051,65535,54970,32,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,12710,65535,65535,42260,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,4258,59196,65503,48599,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,10597,65503,65535,42260,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,6371,61277,65535,48599,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,8484,63422,65535,42260,32,0,
+ 0,0,0,0,0,0,0,0,
+ 29582,27501,14791,6339,32,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,8452,63390,65535,48599,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,6371,63390,65535,42260,32,0,
+ 0,0,0,0,0,0,0,0,
+ 21162,59196,65535,61277,52825,42260,31695,29582,
+ 21162,19017,14791,12678,8484,6339,2145,2145,
+ 2145,2113,32,32,0,0,0,0,
+ 32,10565,63422,65535,48599,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,4226,59196,65535,44373,32,0,
+ 0,0,0,0,0,0,0,0,
+ 32,12710,52857,65503,65535,65535,65535,65535,
+ 65535,65535,65503,65503,65535,65503,65503,61277,
+ 59164,54970,54938,46518,16936,0,0,32,
+ 0,12678,65535,65535,46518,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,2145,54970,65535,44373,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,4258,50744,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 63422,63390,54970,42292,14823,0,0,0,
+ 0,14791,65535,65535,46518,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,48631,65535,42292,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,21130,54970,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65503,59164,
+ 29582,10597,2113,32,0,0,0,32,
+ 0,19017,65535,65535,46518,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,0,44405,65535,42292,0,0,
+ 0,0,0,0,0,0,0,0,
+ 16904,46518,65503,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65503,
+ 38066,8452,0,0,0,0,0,0,
+ 0,21162,65535,65503,46518,32,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,42292,65535,42292,0,0,
+ 0,0,0,0,0,0,32,2145,
+ 52857,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,57051,21130,0,0,0,0,0,
+ 0,23275,65535,65535,44405,32,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,42292,65535,42292,0,0,
+ 0,0,0,0,0,0,0,32,
+ 6339,31695,38066,40179,40147,40179,42292,44373,
+ 46486,46518,48631,50744,54938,57051,59164,59196,
+ 65503,65535,61309,35953,0,0,0,0,
+ 0,25388,65535,65535,44405,32,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,42292,65535,42292,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,32,
+ 0,0,32,32,32,2113,2113,2113,
+ 2113,4258,8452,6339,0,0,0,0,
+ 0,27501,65535,65535,44373,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,42292,65535,42292,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,0,0,0,2113,4226,6339,
+ 6339,6371,6371,6371,6371,6371,8452,6371,
+ 4258,0,0,0,0,0,0,0,
+ 0,29614,65535,65535,44373,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,46518,65535,40179,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,2113,44373,61277,
+ 63390,61309,61277,61309,61309,61277,63390,54970,
+ 16904,0,0,0,0,0,0,0,
+ 0,33808,65535,65535,42292,2113,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,52825,65535,38034,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,4258,23243,
+ 38066,48599,54938,54970,57051,48631,31727,6371,
+ 0,0,0,0,0,0,0,0,
+ 0,33840,65535,65535,42260,32,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,6339,61277,65535,33840,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,2113,2113,32,0,0,0,
+ 0,0,0,32,0,0,0,0,
+ 0,38034,65535,65535,40147,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,32,12710,65503,65535,31695,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,0,0,0,0,0,
+ 0,38066,65535,65503,38066,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,0,31695,65535,65535,27469,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,42260,65535,65535,38066,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,2113,52825,65535,65535,19049,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,44373,65535,65535,38034,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,10597,65535,65535,63390,10565,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,46486,65535,65535,33840,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,33840,65535,65535,61277,4226,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,48599,65535,65535,31727,0,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 2145,52857,65535,65535,50744,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,50744,65535,65535,27501,32,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 10565,63422,65535,65535,35921,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,52825,65535,65535,25356,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 29582,65535,65535,65535,19049,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 2113,54938,65535,65503,19017,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 44373,65535,65535,63390,10565,0,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 2113,57051,65535,65503,14823,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,0,4258,
+ 57083,65535,65535,59164,2113,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 2145,59196,65535,63390,6371,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,12678,
+ 63422,65535,65535,46486,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 4226,59196,65535,61277,4226,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,23275,
+ 65535,65535,65535,31695,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 6339,61309,65535,54938,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,38066,
+ 65535,65535,65503,19049,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 8484,63390,65535,48631,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,2113,52857,
+ 65503,65535,63390,8452,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 10565,65503,65535,40179,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,4258,61309,
+ 65535,65535,57083,4226,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 14791,65535,65535,27501,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,16936,65535,
+ 65535,65535,46518,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 21162,65535,65535,16936,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,29582,65535,
+ 65535,65535,35953,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 33808,65535,63422,12678,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 2113,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,2113,46486,65535,
+ 65535,65535,25356,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,0,0,
+ 42292,65535,61277,6371,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,2145,52857,65535,
+ 65535,65535,16936,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,0,2113,
+ 50744,65535,59164,2113,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,4226,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,6339,61277,65535,
+ 65535,63390,8484,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,4258,
+ 57083,65535,54970,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,8452,57051,42292,14823,6371,
+ 4258,2145,0,0,0,0,0,0,
+ 0,0,0,0,0,8484,63422,65535,
+ 65535,61277,6339,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,6371,
+ 61309,65535,52825,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,33808,65535,65535,65535,61309,
+ 61277,59164,14823,0,0,0,0,0,
+ 0,0,0,0,0,19049,65535,65535,
+ 65535,57083,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,12678,
+ 65535,65535,44373,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,6371,59164,65535,65535,65535,65535,
+ 65535,65535,59196,10565,0,0,0,32,
+ 0,0,0,0,0,27469,65535,65535,
+ 65535,52825,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,21130,
+ 65535,65535,42292,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,38066,65535,65535,65535,65535,59196,
+ 54938,65503,65535,52857,6371,0,0,0,
+ 0,0,0,0,0,38034,65535,65535,
+ 65535,44373,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,33808,
+ 65535,65535,40179,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,27501,65503,65535,65503,65535,54938,10565,
+ 6339,54938,65535,65535,46518,4226,0,0,
+ 0,0,0,0,0,44405,65535,65535,
+ 65535,38066,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,42260,
+ 65535,65535,38066,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,32,0,0,0,2145,
+ 29614,63390,65535,65535,65535,65503,25356,32,
+ 0,21130,65535,65535,65503,35921,0,0,
+ 0,0,0,0,32,50712,65535,65535,
+ 65535,25388,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,50712,
+ 65535,65535,38034,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,8484,44405,
+ 65535,61277,57083,65503,65535,61309,6371,0,
+ 0,8452,63390,65535,65535,63390,16904,0,
+ 0,0,0,0,2113,54970,65535,65535,
+ 65535,21162,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,2113,52857,
+ 65535,65535,35953,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,32,32,31695,61277,65503,
+ 44373,6371,10597,65503,65535,46486,32,0,
+ 0,32,59164,65535,65535,65535,48599,2145,
+ 0,0,0,0,4258,61277,65535,65535,
+ 63422,14791,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,4226,59164,
+ 65535,65535,33840,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,0,32,4258,44405,65535,65503,40147,
+ 2145,32,27469,65535,65503,19049,0,0,
+ 0,0,50712,65535,65535,65503,63422,12710,
+ 0,0,0,0,8452,63390,65535,65535,
+ 63390,10565,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,4258,61309,
+ 65535,65535,31727,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,4258,48631,65535,65535,38066,32,
+ 32,10597,61309,65535,46518,32,0,32,
+ 0,32,38066,65535,65535,65535,65535,33808,
+ 32,0,0,0,12710,65535,65535,65535,
+ 61277,8484,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,6371,63390,
+ 65535,65535,31695,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,2113,44405,65535,65503,44405,32,0,
+ 32,46486,65535,61277,10565,0,32,0,
+ 0,0,31695,65535,65535,65535,65535,50744,
+ 32,0,0,0,19017,65535,65535,65535,
+ 59196,4226,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,6371,61309,
+ 65535,65535,31695,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,29582,65535,65535,54970,6371,0,0,
+ 16904,63422,65503,27469,0,0,0,0,
+ 0,0,27469,65535,65535,65535,65535,61277,
+ 6339,0,0,0,23275,65535,65535,65535,
+ 54970,32,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,6339,61277,
+ 65535,65535,31695,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 10597,63390,65535,63390,14823,0,32,6339,
+ 54970,65535,44373,32,0,32,0,0,
+ 0,0,27469,65535,65535,65535,65535,65535,
+ 14791,32,0,0,27501,65535,65535,65535,
+ 50712,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,2145,4226,8452,10565,8484,4258,
+ 2145,32,0,0,0,0,0,0,
+ 0,0,0,0,0,0,4258,59196,
+ 65535,65535,29614,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,2113,
+ 46518,65535,65535,42260,32,0,8452,48599,
+ 65535,52857,4258,0,0,0,0,32,
+ 32,0,42260,65535,65535,65535,65535,65535,
+ 31727,0,0,0,35953,65535,65535,65535,
+ 42292,32,0,0,0,0,0,0,
+ 0,0,0,0,0,32,32,6371,
+ 27469,44373,54970,63390,63422,65503,65503,63422,
+ 59164,50744,40179,29582,8452,32,0,0,
+ 0,0,0,0,0,0,4226,57083,
+ 65535,65535,29582,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,32,21162,
+ 63422,65535,61277,10565,0,12678,52857,65535,
+ 61277,14791,0,0,0,0,0,0,
+ 2113,21162,63422,65535,65535,65535,65535,65535,
+ 46518,32,0,0,42292,65535,65535,65535,
+ 38034,0,0,0,0,0,0,0,
+ 0,0,0,32,0,6339,38034,61309,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,63390,40147,8484,0,
+ 0,0,0,0,0,0,2113,54970,
+ 65535,65535,29582,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,2145,54970,
+ 65535,65535,31695,0,19049,59196,65535,63390,
+ 21162,32,0,0,0,0,4258,21162,
+ 44405,63390,65535,65535,65535,65535,65535,65535,
+ 54938,2145,0,32,50744,65535,65535,65535,
+ 29582,0,0,0,0,0,0,0,
+ 0,0,32,32,21162,59164,65503,65535,
+ 65535,63390,61309,59164,52857,54970,57083,57083,
+ 63390,65503,65503,65503,65535,65535,59164,14823,
+ 0,0,0,0,0,0,32,52857,
+ 65535,65535,29582,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,31695,65535,
+ 65535,61309,23275,33840,63422,65535,61309,23275,
+ 32,0,0,0,8484,38066,61277,65503,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 61309,10565,32,2145,57051,65535,65535,65535,
+ 23275,0,32,0,0,0,0,0,
+ 0,32,32,27501,61277,52825,35953,25356,
+ 16904,6371,2113,2113,2113,2113,32,0,
+ 6339,12710,23275,35921,52825,63422,65503,61277,
+ 12710,0,0,0,32,0,0,50744,
+ 65535,65535,29582,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,10565,63390,65503,
+ 65535,65535,65535,65535,65535,59164,16936,0,
+ 0,0,4258,29582,61309,65503,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,23275,0,4258,61277,65535,65535,65503,
+ 19017,0,32,0,0,0,0,0,
+ 0,0,6371,21162,4258,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,8484,33808,52857,
+ 44405,2113,0,0,0,0,32,48631,
+ 65535,65535,35921,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2113,46486,65535,65535,
+ 65535,65535,65535,65535,50744,10565,0,0,
+ 0,21162,54970,65503,65503,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,48599,2145,21162,63422,65535,65535,65535,
+ 14791,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,32,
+ 16936,4258,32,32,0,0,0,48631,
+ 65535,65535,42260,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,0,0,2113,19049,63422,65535,65535,
+ 65535,65535,65535,46486,4226,0,32,12710,
+ 46518,65503,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65503,42260,61277,65535,65535,65535,63422,
+ 8452,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,0,0,0,0,48631,
+ 65535,65535,50744,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,4258,57051,65535,65535,65503,
+ 65535,65535,65535,16904,32,10565,40147,65535,
+ 65535,65503,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65503,65535,65535,65503,65535,65535,61277,
+ 6371,0,0,32,0,0,0,0,
+ 0,0,0,0,32,32,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,0,0,0,0,48631,
+ 65503,65535,59196,2145,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,33808,65535,65535,65535,65535,
+ 65535,65535,65535,44405,40179,61277,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,57051,
+ 2145,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,50744,
+ 65535,65535,65503,16936,32,0,0,0,
+ 0,0,0,32,0,0,0,0,
+ 0,0,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,10565,61277,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65503,65503,52857,38066,27469,21130,14791,
+ 12678,16904,23243,29582,42260,54970,65503,65535,
+ 65535,65535,65535,65535,65535,65535,65535,52825,
+ 32,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,52825,
+ 65535,65535,65535,40147,32,0,0,0,
+ 0,32,16936,35921,38034,31695,21130,10565,
+ 2145,32,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,2113,44405,65535,65535,65535,65535,65535,
+ 65535,65535,65503,65535,65535,65535,65535,65535,
+ 61309,42292,14791,4258,8452,6339,2113,0,
+ 32,0,0,0,0,32,16904,42292,
+ 63422,65535,65535,65535,65535,65535,65535,46486,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,2113,2113,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,2113,52857,
+ 65535,65535,65535,59164,10565,8484,12710,19049,
+ 27469,40179,65535,65535,65535,65535,65535,63422,
+ 59196,46486,29614,16936,8484,4226,32,2113,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,21130,65503,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65503,48631,
+ 12710,2145,25388,57051,63422,61309,52857,38034,
+ 19017,4258,0,32,0,0,0,2113,
+ 10597,46518,65535,65535,65535,65535,65535,42292,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,29614,54970,50744,46486,
+ 38034,35921,31727,29614,27501,27469,23275,23243,
+ 19049,16904,10565,6371,0,0,0,0,
+ 0,0,0,0,0,0,2145,54938,
+ 65535,65535,65535,65535,63390,63422,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65503,65535,65535,63390,61277,54938,48599,
+ 33808,19017,2145,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 4258,54970,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,63422,65535,63390,33808,32,
+ 32,8452,46518,57051,63390,65535,65535,65503,
+ 65535,61277,42292,23243,4258,32,0,32,
+ 0,4258,46486,65535,65535,65535,65535,35953,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2113,8452,16936,23275,
+ 29614,33840,42260,46486,50744,54938,59164,61309,
+ 63422,63422,63390,63390,54970,42260,23275,10597,
+ 32,0,0,0,0,0,4226,59164,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65503,65535,65535,
+ 65535,65503,57083,31727,8452,32,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 35921,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,61309,19049,32,0,
+ 0,0,2145,12678,59196,65535,65535,65535,
+ 65535,65535,65535,65503,54970,16904,0,0,
+ 0,0,8452,57083,65535,65535,65535,29614,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,32,32,2113,2145,4258,
+ 10597,16904,25356,29614,40147,44373,50712,54938,
+ 38034,2113,0,0,0,0,6339,59196,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,63422,61277,59164,61277,
+ 61277,63422,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,61277,25388,32,0,
+ 32,32,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,12678,
+ 61309,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,59196,14823,0,32,0,
+ 0,14791,40147,61277,65535,65535,65535,65535,
+ 65535,65535,65535,52857,16936,2113,32,0,
+ 32,0,0,27501,65535,65535,65535,23275,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,32,
+ 0,0,0,0,0,0,32,2145,
+ 4226,0,0,0,0,0,8452,61309,
+ 65535,65535,65535,65535,65535,65535,65535,63390,
+ 50712,38034,21130,16904,16904,16904,12678,6371,
+ 4258,10565,19017,31695,44405,59164,65503,65535,
+ 65535,65535,65535,65535,65535,63422,44373,4258,
+ 0,32,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,2113,48631,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,63390,16936,0,0,0,4258,
+ 42292,63422,65535,65503,65535,65535,65503,65535,
+ 65535,65535,65535,65503,63390,50712,25388,4258,
+ 0,0,32,8484,63390,65535,65535,21162,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,32,32,0,0,8484,63390,
+ 65535,65535,65535,65535,65535,59196,33840,8452,
+ 2113,2145,42292,61277,63422,65503,63390,61309,
+ 54938,38034,23275,10565,2113,2145,12678,33808,
+ 48599,61277,65535,65535,65535,65535,65535,50744,
+ 8484,0,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,27501,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,63390,21130,0,0,0,0,35953,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,54938,
+ 6371,0,0,2113,48631,65535,65535,16936,
+ 0,0,0,0,0,0,0,0,
+ 0,32,0,0,0,0,32,0,
+ 0,32,0,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,10597,65503,
+ 65535,65535,65535,65535,61309,16904,32,0,
+ 0,0,14823,54938,65535,65535,65535,65535,
+ 65503,65503,65503,65503,52857,27501,6371,32,
+ 0,6371,35953,61309,65535,65535,65535,65535,
+ 52857,6371,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,8484,61277,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,31695,0,0,32,0,0,19049,
+ 25356,25356,25356,25356,25356,25356,25356,23275,
+ 23275,23275,23275,25356,23275,23243,23275,23275,
+ 10597,0,0,0,38066,65535,65535,16904,
+ 0,0,0,0,0,0,0,0,
+ 6339,42292,35921,14791,2145,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,14791,65503,
+ 65535,65535,65535,65535,31695,0,0,0,
+ 0,0,0,4226,19017,42292,61309,65535,
+ 65535,65535,65535,65535,65535,65535,61309,46486,
+ 23243,4226,0,8452,44373,65503,65535,65535,
+ 65535,52857,8452,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2113,42292,65535,65535,
+ 65535,65535,65535,65503,65535,65535,65535,65535,
+ 46518,32,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,0,
+ 0,0,0,32,0,0,0,0,
+ 0,0,0,0,25356,65535,65535,14791,
+ 0,0,0,0,0,0,0,0,
+ 0,31695,65503,63422,59164,38034,16936,6339,
+ 32,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,16936,65535,
+ 65535,65535,65535,59164,4226,0,0,32,
+ 0,32,2145,6371,8452,14791,33808,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65503,61277,35921,12710,2145,23243,61277,65535,
+ 65535,65535,50744,4258,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,21130,65535,65535,65535,
+ 65535,65535,61277,31727,65535,65535,65535,65535,
+ 12678,0,0,0,0,0,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,0,0,
+ 32,0,0,0,14791,65535,65535,12710,
+ 0,0,0,0,0,0,0,0,
+ 0,32,33808,65503,65535,65535,65503,61277,
+ 42292,19049,4226,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,19049,65535,
+ 65535,65535,65535,38066,0,0,0,0,
+ 10597,33808,50744,59196,63422,63422,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,63422,
+ 61277,63390,63390,61309,21130,0,16904,59164,
+ 65535,65535,63422,44373,32,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,6371,57083,65503,65535,65535,
+ 65535,63422,27469,19017,65535,65503,65535,42260,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,6339,63390,65503,12678,
+ 0,0,0,0,0,0,0,0,
+ 0,0,2113,35921,65503,65535,65535,65535,
+ 65535,65503,59164,48599,27501,12710,4258,0,
+ 0,0,0,0,0,32,0,0,
+ 0,4226,10565,32,0,0,23243,65535,
+ 65535,65535,63422,16936,0,0,0,0,
+ 0,0,2145,10565,19049,33808,48599,54970,
+ 63422,65535,65535,65535,65535,65535,65535,65503,
+ 40179,12678,6371,8452,2145,0,32,14823,
+ 57083,65535,65535,63422,21130,0,0,0,
+ 0,0,0,0,0,0,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,40179,65535,65535,65535,65535,
+ 65535,44405,2113,21162,65535,65535,63422,12710,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2113,57051,65503,10597,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,2113,27501,61277,65535,65535,
+ 65535,65535,65535,65503,65535,65503,59196,48631,
+ 38034,31695,27469,27501,29582,29582,31695,31727,
+ 44373,44373,8452,0,0,0,23275,65535,
+ 65535,65535,59196,4226,0,0,0,0,
+ 0,0,0,0,32,0,32,2145,
+ 8452,23243,40147,48599,57083,63422,65535,65535,
+ 65535,61277,33808,10597,0,0,0,0,
+ 14791,57083,65535,65535,50744,32,0,0,
+ 0,32,0,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,23243,65535,65535,65535,65535,65535,
+ 54970,8452,0,27501,65503,65535,50744,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,50744,63422,10565,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,12678,50744,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65503,65535,63422,
+ 33840,4226,0,0,0,0,25356,65535,
+ 65535,65535,44373,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,32,4226,12678,25356,38066,
+ 52825,63390,65503,63390,33840,32,0,0,
+ 0,10565,54938,65535,65503,16904,0,0,
+ 2113,19017,35921,38066,29582,10597,32,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,10565,59196,65535,65535,65503,65535,63422,
+ 16936,0,0,35921,65535,65535,29614,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,48599,65503,8484,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,2145,27469,
+ 54938,65503,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,61277,42260,10597,
+ 32,0,0,0,0,0,25388,65535,
+ 65535,65535,27469,0,0,0,0,0,
+ 0,0,0,32,0,0,0,0,
+ 0,0,0,0,32,0,32,0,
+ 2113,6339,14823,27501,42260,19049,32,0,
+ 0,0,8452,52857,65535,44405,32,8452,
+ 46486,63422,65535,65535,65535,61277,44405,14823,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 4258,50712,65535,65535,65503,65535,65503,27501,
+ 0,0,0,44373,65535,63390,12678,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,46486,63422,10565,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 2145,19017,44373,57083,63390,65503,65535,65503,
+ 65535,65503,59164,50744,27501,6371,32,0,
+ 0,0,0,0,0,0,23243,65535,
+ 65535,65535,19049,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,8484,57083,63390,14823,52825,
+ 65535,65535,65535,65535,65535,65535,65503,63422,
+ 44405,10565,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,0,32,
+ 33840,65535,65535,65535,65535,65535,42260,32,
+ 0,32,32,50744,65503,61309,2145,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,48599,63390,8452,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,2145,8452,14823,21162,21162,
+ 16936,10597,4226,32,0,0,0,0,
+ 0,0,0,0,0,0,19049,65535,
+ 65535,65535,16936,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,19049,63422,61277,65535,
+ 65535,65535,65535,63422,61277,63422,65535,65535,
+ 65535,61277,33840,2113,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,19049,
+ 63390,65535,65535,65535,65535,52825,4258,0,
+ 0,32,4226,59196,65535,48599,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,52857,63390,6371,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,0,0,32,0,0,0,0,
+ 0,0,0,0,0,0,32,0,
+ 0,0,0,0,0,0,14823,65535,
+ 65535,65503,14791,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,32,32,50744,65535,65535,
+ 65535,65535,57083,19049,4226,8452,23275,50744,
+ 65535,65535,65503,52857,16936,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,10565,59164,
+ 65535,65535,65535,65535,59196,10597,0,0,
+ 0,0,8452,65503,65535,35953,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2145,57051,63422,6371,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,12678,65535,
+ 65535,65503,12678,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,33840,65535,65535,
+ 65535,57083,10597,0,0,32,0,2113,
+ 25356,59196,65535,65535,63390,33840,4226,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,0,0,0,0,2113,48599,65535,
+ 65535,65535,65535,63390,21130,0,32,0,
+ 0,0,21130,65535,65535,27501,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,4258,61277,63390,6371,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,8484,63390,
+ 65535,63422,10597,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,19049,65535,65535,
+ 65503,16936,2145,16936,29614,29614,21162,10565,
+ 2145,10597,52825,65535,65535,65535,54938,14823,
+ 32,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,33840,65503,65535,
+ 65535,65535,65535,33808,2113,0,0,0,
+ 0,0,31695,65503,65535,23243,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,10565,63422,61309,6339,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,8452,61309,
+ 65535,63390,10565,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,14823,63422,65535,
+ 48631,32,29614,59164,65535,65535,65535,65503,
+ 52857,33840,23243,61309,65535,63422,65535,63390,
+ 31695,2145,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,0,0,19049,65503,65535,65535,
+ 65535,65503,46486,2113,0,0,0,0,
+ 0,0,42292,65503,63422,16904,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,16936,65535,61309,6339,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,6371,61277,
+ 65535,61309,8484,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,8484,61309,65535,
+ 33808,0,0,6371,31695,54938,65535,65535,
+ 65535,65535,65503,63422,65535,65535,65535,65535,
+ 65535,48599,8484,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,6371,59164,65535,65535,65535,
+ 65535,57083,6371,0,0,0,0,0,
+ 0,2145,48631,65535,63390,12678,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,25356,63422,61309,6371,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,6339,59196,
+ 65535,61277,8452,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,4226,59196,65535,
+ 21130,0,32,32,0,2113,21130,44405,
+ 61309,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,59164,21162,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,2113,44373,65535,65535,65535,65535,
+ 61277,16936,0,0,0,0,0,0,
+ 0,2113,54970,65535,63390,6339,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,33840,65535,61277,6339,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,6339,61277,
+ 65535,61309,8452,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,2145,59164,65503,
+ 10565,0,0,0,0,0,0,32,
+ 8484,31727,57051,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65503,33808,2113,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,27469,63390,65503,65535,65535,65535,
+ 31695,0,0,0,0,0,0,0,
+ 0,4258,59196,65535,61277,2145,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,40179,65535,61277,4258,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,6371,61309,
+ 65535,61277,6371,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,2145,59164,61309,
+ 6339,0,0,0,0,0,0,32,
+ 4258,4226,8484,42292,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65503,42260,2145,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,10597,61277,65535,65535,65535,65503,46486,
+ 2113,0,0,0,0,0,0,0,
+ 0,8452,63390,65535,52857,32,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,46518,65535,61277,4226,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,8484,63390,
+ 65535,59196,6339,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,2145,59164,57051,
+ 2145,0,32,0,32,0,32,4258,
+ 50744,61309,61309,63422,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,40147,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 2113,46486,65535,65535,65535,65535,59164,8484,
+ 0,0,32,0,0,0,0,0,
+ 0,23243,65503,65535,48599,2113,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,50744,65535,61309,6339,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,10565,63422,
+ 65535,59164,4258,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,2145,59164,52857,
+ 32,0,0,0,0,0,0,0,
+ 12678,52825,63422,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,63390,23243,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 23275,65535,65535,65535,65535,65503,21162,0,
+ 0,0,0,0,0,0,0,2113,
+ 12678,54970,65535,65535,42260,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,2113,52857,65535,61277,4258,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,12678,63422,
+ 65535,59164,4258,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,4258,59196,48599,
+ 0,0,0,0,0,0,0,0,
+ 0,2113,16904,29582,44373,54970,63390,65535,
+ 65535,65535,65535,65535,65535,65535,65535,57083,
+ 2113,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,4258,
+ 57083,65535,65535,65535,65535,44373,32,0,
+ 0,0,0,0,0,0,0,31727,
+ 65503,65535,65535,65535,33840,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,2113,52857,65535,61277,4258,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,12710,65503,
+ 65535,57083,4226,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,6339,61277,46486,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,0,0,32,10565,31727,
+ 54938,65503,65503,65535,65535,65535,65535,65503,
+ 21162,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,29582,
+ 65535,65535,65535,65535,63390,12678,0,0,
+ 0,0,0,0,0,0,0,12678,
+ 63422,65535,65535,65535,25388,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,2113,52857,65535,61277,4258,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,16904,65535,
+ 65535,57051,2145,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,8452,61309,44405,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,0,
+ 2145,25388,57083,65535,65535,65535,65535,65535,
+ 38066,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,2145,54970,
+ 65535,65535,65535,65535,42292,0,32,0,
+ 0,0,0,0,0,0,0,32,
+ 50712,65535,65535,65503,19049,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,2113,52857,65535,61277,4258,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,19017,65535,
+ 65535,54970,2113,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,8484,63390,44405,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,0,6371,48599,65503,65535,65535,65535,
+ 50744,32,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,19017,65535,
+ 65535,65535,65535,63422,14791,32,0,0,
+ 0,0,0,0,2113,19049,10565,2113,
+ 31695,65535,65535,65535,12678,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,2113,52857,65535,61277,2145,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,23243,65535,
+ 65535,54970,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,12678,63390,50712,
+ 32,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,2113,48599,65535,65535,65535,
+ 57083,2113,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,32,42292,65535,
+ 65535,65535,65535,57051,2145,0,0,0,
+ 0,0,0,0,4258,59196,63390,54970,
+ 50744,65535,65535,63390,6371,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,32,50744,65535,59196,2145,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,25356,65535,
+ 65535,52857,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,14791,65535,52857,
+ 32,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,8452,54938,65535,65535,
+ 61277,32,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,2113,59196,65535,
+ 65503,65535,65535,44373,32,0,0,0,
+ 0,0,0,0,32,44405,65535,65535,
+ 65503,65535,65535,59196,2145,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,32,48599,65535,59164,2145,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,29582,65535,
+ 65535,50744,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,19049,65535,54970,
+ 32,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,19049,65503,65535,
+ 65503,6339,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,12710,63390,65535,
+ 65535,65535,65535,33840,0,0,0,0,
+ 0,0,0,0,0,16936,65503,65535,
+ 65535,65535,65535,59164,2145,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,32,46518,65535,59164,2145,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,31727,65535,
+ 65535,50744,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,25356,65503,57051,
+ 2113,32,0,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,48599,65535,
+ 65535,10597,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,19049,65535,65535,
+ 65535,65535,65535,27469,0,0,0,0,
+ 0,0,0,0,0,2145,54938,65535,
+ 65535,65535,65535,54938,32,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,46486,65535,59164,2145,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,33840,65535,
+ 65535,48599,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,29614,65535,57051,
+ 2113,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,23275,65503,
+ 65503,16904,0,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,25356,65535,65535,
+ 65535,65535,65535,23275,0,0,0,0,
+ 0,0,0,0,0,0,27501,65503,
+ 65535,65535,65535,48631,32,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,44373,65535,59164,2113,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,38034,65535,
+ 65535,46486,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,35921,65535,57051,
+ 32,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,8452,63390,
+ 65535,21162,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,27469,65535,65535,
+ 65535,65535,65535,25388,0,0,0,0,
+ 0,0,0,0,0,0,6339,59164,
+ 65535,65535,65535,42260,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,42260,65535,59164,2113,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,42260,65535,
+ 65535,44405,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,42260,65535,54970,
+ 32,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,2113,54970,
+ 65535,29582,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,25356,65535,65535,
+ 65535,65535,65535,31695,0,0,0,0,
+ 0,0,0,0,0,0,0,44373,
+ 65535,65535,65535,38034,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,40179,65535,59164,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,44405,65535,
+ 65535,44373,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,44373,65535,52857,
+ 32,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,44373,
+ 65535,33840,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,16936,65535,65535,
+ 65535,65535,65535,44405,32,0,0,0,
+ 0,0,0,0,0,0,0,21162,
+ 65503,65535,65535,29582,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,38066,65535,57083,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,46518,65535,
+ 65535,40147,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,50712,65535,50744,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,35953,
+ 65535,40147,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,10597,63422,65535,
+ 65535,65535,65535,54938,2145,0,0,32,
+ 0,0,0,0,0,32,0,8484,
+ 65535,65535,65503,21162,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,38066,65535,57083,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,50712,65535,
+ 65535,38066,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,52857,65535,46518,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,29614,
+ 65535,44373,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,4258,57051,65535,
+ 65535,65535,65535,65503,10565,0,32,0,
+ 0,0,0,0,0,0,0,4258,
+ 61277,65535,63422,16904,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,38066,65535,57083,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,2113,54938,65535,
+ 65535,35953,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2113,57083,65535,40179,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,27469,
+ 65535,50712,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,33840,65535,
+ 65535,65535,65535,65535,33808,0,0,0,
+ 0,0,0,0,0,0,0,2145,
+ 59164,65535,63390,10597,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,38066,65535,57083,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,2113,57083,65535,
+ 65535,35921,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,4258,61277,65535,33840,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,27501,
+ 65535,54938,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,10597,61309,
+ 65535,65535,65535,65535,57083,2145,0,0,
+ 0,0,0,0,0,0,0,32,
+ 57051,65535,63390,4226,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,38034,65535,57051,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,4258,61309,65535,
+ 65503,33808,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,6339,61309,65535,27501,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,29582,
+ 65535,57083,2145,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,42260,
+ 65535,65535,65535,65503,65535,25356,0,0,
+ 0,0,0,0,0,0,0,2145,
+ 59164,65535,59164,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,38034,65535,57051,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,6339,61309,65535,
+ 65535,31695,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,10565,65503,65535,19049,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,35953,
+ 65535,61277,4226,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,10565,
+ 61277,65535,65535,65535,65535,54938,2145,0,
+ 0,0,0,0,0,0,0,4226,
+ 61309,65535,48599,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,38034,65535,57051,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,8452,63390,65535,
+ 65535,29614,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,14791,65503,65503,12678,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,46518,
+ 65535,63390,6339,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,0,32,
+ 27501,65535,65535,65535,65535,65535,27501,0,
+ 0,0,0,0,0,0,0,6339,
+ 65503,65535,40147,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,38034,65535,57051,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,8484,63390,65535,
+ 65535,29582,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,21162,65535,63390,6339,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,54938,
+ 65535,63390,8484,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 2113,46486,65535,65535,65535,65535,59196,8484,
+ 0,0,0,0,0,0,0,14823,
+ 65535,65535,29582,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,38034,65535,54970,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,8484,63422,65535,
+ 65535,27469,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,25388,65535,57083,2113,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,4226,61309,
+ 65535,63422,10597,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,6371,57083,65535,65535,65535,65535,46486,
+ 2145,0,0,0,0,0,32,29582,
+ 65535,65535,21130,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,38034,65535,54970,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,8484,63422,65535,
+ 65535,25388,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,29614,65535,54938,2113,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,6371,63390,
+ 65535,65503,14791,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,19049,63422,65535,65535,65535,65535,
+ 27501,0,0,0,32,0,0,40179,
+ 65535,63390,10597,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,35953,65535,54970,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,8484,63422,65535,
+ 65535,23275,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,35921,65535,48631,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,12678,65503,
+ 65535,65503,19049,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,35953,63422,65535,65535,65535,
+ 61309,12678,0,0,0,0,32,50744,
+ 65535,61277,6339,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,35953,65535,54970,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,8484,63422,65535,
+ 65535,23243,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,40147,65535,46486,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,16904,65535,
+ 65535,65503,23243,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,4226,52825,65535,65535,65535,
+ 65535,50712,4258,0,0,0,4258,59164,
+ 65535,52857,32,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,35921,65535,54970,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,6339,63390,65535,
+ 65535,21162,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,44405,65535,42292,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,19049,65535,
+ 65535,65535,29582,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,32,12678,61277,65535,65535,
+ 65535,65535,40147,2113,32,0,21130,65535,
+ 65535,44405,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,35953,65535,54938,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,4258,61309,65535,
+ 65535,19049,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,48599,65535,42292,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,19049,65535,
+ 65535,65535,31727,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,31695,63422,65535,
+ 65535,65535,65503,42292,16936,16904,54970,65535,
+ 65535,33840,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,38066,65535,52857,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,4226,61277,65535,
+ 65535,16936,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,50744,65535,42292,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,19049,65535,
+ 65535,65535,35953,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,4226,52825,65535,
+ 65535,65535,65535,65535,65535,65503,65535,65535,
+ 65535,27469,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,38066,65535,52857,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,2145,59196,65535,
+ 65503,16904,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,54938,65535,44373,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,19049,65535,
+ 65535,65535,40179,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,16936,63422,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,16936,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,38066,65535,52857,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,57083,65535,
+ 65503,14791,0,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,2113,57083,65535,44373,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,16904,65535,
+ 65535,65535,42260,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,32,44405,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65503,10565,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,42260,65535,52825,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,54938,65535,
+ 65503,14791,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,2145,59164,65535,44405,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,14791,65535,
+ 65535,65535,44373,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,10597,
+ 63390,65535,65503,65535,65535,65535,65535,65535,
+ 63390,6339,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,44405,65535,52825,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,50712,65535,
+ 65503,14791,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,4258,59164,65535,46486,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,12678,65535,
+ 65535,65535,46486,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,0,0,
+ 40179,65535,65535,65535,65535,65535,65535,65535,
+ 61277,6371,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,48599,65503,52825,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,46518,65535,
+ 63422,12710,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,6339,59196,65535,48599,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,10597,65503,
+ 65535,65535,48599,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 10597,63390,65535,65535,65535,65535,65535,65535,
+ 61277,6339,0,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,4226,54938,65535,52825,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,44373,65535,
+ 63422,12678,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,0,0,6339,59196,65535,50712,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,8484,63422,
+ 65535,65535,50712,2113,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,44373,65535,65535,65535,65535,65535,65535,
+ 61309,4258,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,6339,61277,65535,50712,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,40179,65535,
+ 63422,12678,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,6371,61277,65535,50744,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,8484,63422,
+ 65535,65535,50712,2113,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,12678,63422,65535,65535,65535,65535,65535,
+ 63422,8452,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,12710,65535,65535,50712,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,35953,65535,
+ 63422,12678,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,8452,61309,65535,52857,2113,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,8484,63422,
+ 65535,65535,50712,2113,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,48599,65503,65535,65535,65535,65535,
+ 65503,12710,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,21162,65535,65535,50744,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,33840,65535,
+ 65503,12710,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,8452,63390,65535,54938,2113,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,8484,63422,
+ 65535,65535,50712,2113,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,19017,63422,65535,65535,65535,65503,
+ 65503,19049,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,38034,65535,65535,48631,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,31695,65535,
+ 63422,16936,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,8484,63390,65535,57083,2145,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,10565,65535,
+ 65535,65535,44405,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,2145,52857,65535,65535,65535,65535,
+ 65535,23243,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,0,32,52825,65535,65535,48631,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,29582,65535,
+ 65535,19049,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,10565,63422,65535,59196,4258,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,10597,65503,
+ 65535,65535,40179,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,27469,65503,65535,65535,65535,
+ 65535,27469,0,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,4258,57051,65535,65535,48631,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,27469,65535,
+ 65535,25356,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,12678,65503,65535,63390,8484,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,19017,65503,
+ 65535,65535,31695,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,6339,59164,65535,65535,65535,
+ 65535,27501,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,10565,61309,65535,65535,48631,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,25388,65535,
+ 65535,31695,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,12710,65535,65535,63422,10597,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,27469,65535,
+ 65535,65535,21162,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,35953,65535,65535,65535,
+ 65535,29582,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,12678,65503,65535,65535,57051,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,23243,65535,
+ 65535,42260,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,14823,65503,65535,65535,16904,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,32,48599,65535,
+ 65535,63422,14791,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,8484,61309,65535,65535,
+ 65535,27501,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,16904,65535,65535,65535,61277,2145,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,23243,65535,
+ 65535,50744,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,16936,65503,65535,63422,21130,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,16904,65503,65535,
+ 65535,61309,4226,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2113,52857,65535,65535,
+ 65535,25388,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,16904,65535,65535,65535,65503,12678,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,23243,65535,
+ 65535,59196,4258,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,25356,65535,65535,65535,27469,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,4258,50744,65535,65535,
+ 65535,63390,6339,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,48599,65535,65535,
+ 65535,25356,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,12678,65503,65535,65535,65535,23243,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,23243,65535,
+ 65535,63390,10597,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,35921,65535,65535,65535,33840,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,25356,65503,65503,65535,
+ 65535,65503,19017,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,48599,65535,65535,
+ 65535,19049,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,6339,59196,65535,65535,65535,35953,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,23243,65535,
+ 65535,65535,19049,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,52825,65535,65535,65535,44373,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,42292,65535,65535,65535,
+ 65535,65535,38066,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2113,50744,65535,65535,
+ 65503,16936,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,42260,65535,65535,65535,38066,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,25388,65535,
+ 65535,65535,31727,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,0,4258,59196,65535,65535,65535,54970,
+ 2113,0,0,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,35953,65535,65535,65535,
+ 65535,65535,54970,2113,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2145,54970,65535,65535,
+ 65503,14791,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,12678,63390,65535,63390,19049,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,29614,65535,
+ 65535,65535,44373,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,0,10597,63422,65535,65535,65535,65503,
+ 12678,0,0,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,12678,63390,65535,65535,
+ 65535,65535,63422,8484,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,4226,59164,65535,65535,
+ 63422,12678,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,33808,63422,29614,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,33808,65535,
+ 65535,65503,48631,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,14823,63390,65535,65535,65535,65535,
+ 25388,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,42260,65535,65535,
+ 65535,65535,65535,25356,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,4258,61309,65535,65535,
+ 63422,10597,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2145,21162,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,23243,63390,
+ 65535,65535,42292,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,14791,63422,65535,65535,65535,65535,
+ 46518,32,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,10565,63390,65535,
+ 65535,65535,65535,40179,32,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,4258,61309,65535,65535,
+ 63422,10597,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,21162,
+ 59164,63390,19017,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,2145,52825,65535,65535,65535,65535,
+ 57083,4258,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,31695,65503,
+ 65535,65535,65535,54938,32,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,6371,63390,65535,65535,
+ 65503,12678,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,32,
+ 6339,12710,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,8452,40147,57083,61309,59164,
+ 31727,2145,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,4226,50744,
+ 65503,65535,65535,59196,32,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,8452,63422,65535,65535,
+ 65503,12678,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,2145,6371,4226,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,10565,
+ 61277,65535,65535,63422,32,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,6339,63422,65535,65535,
+ 65503,16936,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 38034,65535,65535,57083,32,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,4258,61309,65535,65535,
+ 65535,19049,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 19017,65535,65535,48631,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,4258,59196,65535,65535,
+ 65535,21162,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 21130,65535,65535,42260,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,4258,59164,65535,65535,
+ 65535,23243,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 33808,65535,65535,31695,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2113,52857,65535,65535,
+ 65535,21162,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,32,
+ 48631,65535,65503,19017,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,48631,65535,65535,
+ 65535,21162,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,4258,
+ 61309,65535,63422,8484,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,40147,65535,65535,
+ 65535,23275,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,21162,
+ 65503,65535,57083,2145,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,29582,65503,65535,
+ 65535,25388,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,12710,
+ 2145,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,14823,25356,6371,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,40179,
+ 65535,65535,44373,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,16936,65503,65535,
+ 65503,27501,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,32,0,0,10597,54938,
+ 6339,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,12710,63390,59164,19049,0,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,4258,59196,
+ 65535,65503,21130,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,8484,65503,65535,
+ 65535,31727,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,32,0,10565,54938,63390,
+ 8452,0,0,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,2113,50744,65535,61309,14791,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,21130,65535,
+ 65535,59196,4258,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,2113,52857,65503,
+ 65535,40179,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,32,0,0,12710,57051,65535,61277,
+ 6339,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,27469,65535,65535,50744,2113,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,46486,65535,
+ 65535,42260,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,38034,65535,
+ 65535,48631,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,14791,59164,65535,65535,52857,
+ 32,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,4258,52825,65503,65503,19017,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,14823,65503,65535,
+ 63390,10565,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,19017,65535,
+ 65535,59164,4258,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,14823,59196,65535,65535,65535,35921,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,14823,65503,65535,44373,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2145,50744,65535,65535,
+ 35921,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,4226,57083,
+ 65535,65503,16904,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 2113,8452,59164,65503,65535,65535,61277,6371,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,32,48599,65535,54970,2113,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,32,33840,65535,65535,52857,
+ 4226,0,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,40147,
+ 65535,65535,35921,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,40179,65535,65535,65535,65535,31695,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,31727,65535,59196,2145,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,25388,65503,65535,57051,8484,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,12710,
+ 65503,65535,59164,6339,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 8452,63390,65535,65535,65535,54938,6339,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,27469,65535,59164,4226,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,2145,27469,63390,65503,57051,14791,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 40147,65535,65503,31727,32,0,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 19049,65503,65535,65535,65503,25388,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,32,40147,65535,52825,32,
+ 0,0,0,0,0,32,32,0,
+ 0,0,0,0,0,32,0,32,
+ 8452,44373,65535,65535,52857,8484,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 6339,57083,65503,59164,6371,32,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 35921,65535,65535,65535,59164,4226,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2113,10597,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,4258,57083,65535,33808,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,4258,29614,
+ 59164,65535,65535,52825,6371,0,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,16936,63390,65535,44373,2113,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,32,
+ 50744,65535,65535,65535,40147,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2113,42260,19017,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,23275,65535,61277,8452,0,
+ 0,0,0,0,0,0,0,32,
+ 0,0,32,4226,14823,35953,61277,65535,
+ 65535,65535,48599,6339,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,35921,65535,65503,23275,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,2145,
+ 59164,65535,65535,65535,25356,0,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,19049,61277,21162,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,44405,65503,29614,32,0,
+ 0,0,0,0,0,0,32,4226,
+ 14823,27469,46486,61277,65503,65535,65535,65535,
+ 65503,38066,2145,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,2113,48631,65535,59196,12710,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,6339,
+ 61277,65535,65535,63422,19017,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,52857,63390,
+ 23275,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,0,21162,33808,2113,0,32,
+ 0,0,0,0,2145,27469,50744,61277,
+ 65535,65535,65535,65535,65535,65535,65535,59164,
+ 25388,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,12678,61309,65535,54938,8452,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,6371,
+ 61277,65535,65535,65535,19049,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,25388,65503,
+ 61309,25356,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,0,2113,32,0,0,0,
+ 0,32,0,19017,54938,65503,65535,65535,
+ 65535,65535,65503,65535,65535,61309,42292,8484,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,27469,65503,65535,48631,
+ 4258,0,0,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,4258,
+ 59196,65535,65535,65535,23275,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,4258,59196,
+ 65535,63390,14823,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,32,0,
+ 0,32,29614,63422,65535,65503,65535,65535,
+ 65503,65503,61277,54938,35953,10565,0,0,
+ 0,0,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,0,0,0,2113,46486,65535,65535,
+ 48631,6339,32,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,0,4226,
+ 57083,65535,65535,65535,31695,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,38066,
+ 65535,65535,52857,2113,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 2145,40147,65535,65535,61277,42292,23275,14791,
+ 12710,10597,6339,32,0,0,0,0,
+ 0,0,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,6371,57051,65503,
+ 65535,52857,8484,32,0,0,32,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 46486,65535,65535,65535,42292,0,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,14791,
+ 65503,65535,65503,14823,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,0,4226,
+ 44373,65503,65503,42260,6371,32,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,14823,63390,
+ 65535,65535,59196,19017,0,0,32,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 23243,65535,65535,65535,48631,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,4226,
+ 59196,65535,65535,25356,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,4226,46486,
+ 65535,63422,33840,2113,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,27469,
+ 63422,65535,65535,63390,35921,6371,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 2145,54938,65535,65535,57051,2113,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,2145,
+ 57051,65535,65535,25388,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,2145,42292,65535,
+ 65535,44373,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,0,0,
+ 31727,65503,65535,65535,65535,57051,21162,2145,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,23243,63422,65535,61277,4258,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,2145,
+ 57083,65535,63422,16904,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,40179,65535,65535,
+ 57083,8452,0,32,0,0,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,29614,65503,65535,65535,65535,65535,50744,
+ 21162,2113,0,0,0,32,0,0,
+ 0,32,0,32,0,0,0,0,
+ 0,32,40147,65535,63422,6371,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,4226,
+ 59164,65503,57051,2145,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,31695,65535,65535,65535,
+ 25356,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,19049,59164,65535,65535,65535,65535,
+ 65503,52825,27469,8452,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,4258,48631,63390,6371,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,6339,
+ 61309,65503,25356,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,23275,63422,65535,65535,46486,
+ 2113,0,0,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,6339,38066,61277,65535,65535,
+ 65535,65535,65535,61277,46486,21130,4258,32,
+ 32,0,0,0,0,0,0,0,
+ 0,0,0,10597,52825,4226,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,4226,
+ 54938,33840,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,16936,61309,65535,65535,61277,8484,
+ 32,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,10597,38034,59196,
+ 65535,65535,65535,65535,65535,65535,61277,46518,
+ 27469,12710,2145,32,0,0,0,0,
+ 0,0,0,0,10565,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 4226,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,10565,57051,65535,65535,65503,23243,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,8484,
+ 31727,57083,65503,65535,65535,65535,65535,65535,
+ 65503,65503,57051,44373,19049,4258,0,0,
+ 32,32,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 6339,50744,65535,65535,65535,31727,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,4226,23243,50712,63390,65535,65535,65535,
+ 65535,65535,65535,65535,63422,52857,19049,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,4258,
+ 48599,65535,65535,65503,40147,2113,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,0,0,0,
+ 0,0,0,2113,14791,40179,61277,65535,
+ 65535,65535,65535,65535,65535,65503,65503,38034,
+ 4226,0,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,2145,42260,
+ 65503,65535,65503,46518,2145,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,6371,29614,
+ 52857,65503,65535,65535,65535,65535,65535,65503,
+ 48631,8452,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,4258,46486,65503,
+ 65535,65535,50744,6371,0,0,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 4226,19049,46486,63390,65535,65535,65535,65535,
+ 65535,52857,10565,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,32,8452,46518,65535,65535,
+ 65535,54970,8484,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,10565,33840,52857,65535,65535,
+ 65503,65535,54970,10597,0,32,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,14823,54938,65535,65535,65535,
+ 57083,10597,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,32,0,32,0,4226,21130,50712,
+ 65535,65535,65535,57051,12710,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,4258,35921,63390,65535,65535,65503,59164,
+ 14823,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,0,2113,
+ 29614,63422,65535,65535,54970,10597,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,4226,
+ 27469,54970,65535,65535,65535,65535,61309,19017,
+ 0,0,0,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,27469,65535,65535,65535,54970,12678,0,
+ 0,32,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2113,10565,31695,59164,
+ 65535,65535,65535,65535,65535,61309,23275,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,38034,65535,65535,65535,57083,12678,
+ 32,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,2113,4226,
+ 4226,4258,8452,10597,12678,14791,12678,6371,
+ 6339,4258,2145,2113,2113,0,0,2113,
+ 2145,4258,16936,33808,52825,63390,65535,65535,
+ 65535,65535,65535,65535,63422,27469,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,8452,59164,65535,65535,65535,54938,
+ 8484,0,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,2145,10597,29582,40147,44405,52825,57083,
+ 61309,63422,65535,65535,65535,65535,65503,63422,
+ 63422,61277,54970,50744,48631,46518,46518,48631,
+ 57083,63390,65535,65535,65535,65503,65535,65535,
+ 65535,65535,65535,65503,29614,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,0,31727,65503,65535,65535,65535,
+ 54970,10565,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,6339,
+ 31695,54938,65503,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,63422,33840,32,32,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,6371,57083,65503,65535,65503,
+ 65503,54970,10565,0,0,32,0,32,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,32,8452,35953,59196,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,63390,25388,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,27469,65535,65535,65535,
+ 65535,65535,57051,10597,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,6339,31727,59196,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65503,
+ 63390,59196,54970,52825,50744,48631,48599,48599,
+ 50712,54970,61309,65503,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65503,
+ 48631,12710,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,2145,48631,65535,65503,
+ 65535,65535,65535,57083,14791,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,2113,21162,54970,65503,65535,65535,65535,
+ 65535,65535,65535,65535,65503,54970,40179,21162,
+ 6371,2113,0,32,0,0,0,32,
+ 0,0,6371,19017,38034,50712,61309,65503,
+ 65535,65535,65535,65535,65503,61277,48599,19049,
+ 32,32,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,10565,59164,65535,
+ 65535,65503,65535,65535,61277,21130,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 16904,48599,65503,65535,65535,65535,65535,65535,
+ 65535,65535,63422,46486,16936,2145,0,32,
+ 0,32,0,0,32,0,0,0,
+ 0,0,0,0,0,32,6371,14823,
+ 25388,31695,31695,25388,16904,6371,32,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,16936,61309,
+ 65535,65535,65503,65535,65535,63390,35953,6371,
+ 0,0,32,0,32,0,0,0,
+ 0,0,0,0,2113,6371,25356,46518,
+ 65503,65535,65535,65535,65535,65535,65535,61309,
+ 50712,33808,10597,2113,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,32,21162,
+ 61309,65535,65535,65535,65535,65535,65535,61277,
+ 35921,14823,4258,32,32,0,0,32,
+ 2145,4258,10597,27469,46486,61309,65503,65503,
+ 65535,65535,65503,65535,65535,54970,25356,4258,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,32,0,0,
+ 21162,63390,65535,65535,65535,65535,63422,65535,
+ 65535,63390,59196,52857,48599,44405,44405,48599,
+ 52857,61277,63390,65503,65535,65535,65535,65535,
+ 65535,65535,65535,65503,50744,8452,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,21130,59164,65503,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,63422,61277,50744,10597,0,32,0,
+ 0,32,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 32,0,10597,50744,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,65535,65535,61277,46486,
+ 31695,19017,4258,2113,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,32,0,2145,29582,57083,63422,65535,
+ 65535,65535,65535,65535,65535,65535,65535,65535,
+ 65535,65535,65535,65535,52857,25356,4258,2113,
+ 0,0,0,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,4258,27501,48631,
+ 61309,65503,65503,65535,65535,65535,65503,63422,
+ 63390,59196,44405,23243,2113,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,32,
+ 4258,16904,25388,33840,35953,35921,29614,23243,
+ 10597,4226,32,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0
+};
+
+const basic_image<unsigned short> respect(height,width,pixelData);
\ No newline at end of file
diff --git a/src/tests/drivers/xbee/gui/res/respect.h b/src/tests/drivers/xbee/gui/res/respect.h
new file mode 100644
index 000000000..543531b60
--- /dev/null
+++ b/src/tests/drivers/xbee/gui/res/respect.h
@@ -0,0 +1,11 @@
+
+//This file has been automatcally generated by pngconverter utility
+//Please do not edit
+#ifndef RESPECT_H
+#define RESPECT_H
+
+#include "mxgui/image.h"
+
+extern const mxgui::Image respect;
+
+#endif //RESPECT_H
diff --git a/src/tests/drivers/xbee/logdecoder/.gitignore b/src/tests/drivers/xbee/logdecoder/.gitignore
new file mode 100644
index 000000000..8b193293c
--- /dev/null
+++ b/src/tests/drivers/xbee/logdecoder/.gitignore
@@ -0,0 +1 @@
+logdecoder
\ No newline at end of file
diff --git a/src/tests/drivers/xbee/logdecoder/LogTypes.h b/src/tests/drivers/xbee/logdecoder/LogTypes.h
new file mode 100644
index 000000000..2b7b17bae
--- /dev/null
+++ b/src/tests/drivers/xbee/logdecoder/LogTypes.h
@@ -0,0 +1,68 @@
+/* Copyright (c) 2015-2018 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <fstream>
+#include <iostream>
+
+#include "drivers/Xbee/APIFramesLog.h"
+#include "drivers/Xbee/XbeeStatus.h"
+#include "drivers/xbee/Mark.h"
+#include "drivers/xbee/XbeeTestData.h"
+#include "logger/Deserializer.h"
+#include "logger/LogStats.h"
+
+// Serialized classes
+using std::ofstream;
+
+template <typename T>
+void print(T& t, ostream& os)
+{
+ t.print(os);
+}
+
+template <typename T>
+void registerType(Deserializer& ds)
+{
+ ds.registerType<T>(print<T>, T::header());
+}
+
+void registerTypes(Deserializer& ds)
+{
+ registerType<LogStats>(ds);
+
+ registerType<Xbee::APIFrameLog>(ds);
+ registerType<Xbee::ATCommandFrameLog>(ds);
+ registerType<Xbee::ATCommandResponseFrameLog>(ds);
+ registerType<Xbee::ModemStatusFrameLog>(ds);
+ registerType<Xbee::TXRequestFrameLog>(ds);
+ registerType<Xbee::TXStatusFrameLog>(ds);
+ registerType<Xbee::RXPacketFrameLog>(ds);
+ registerType<Xbee::XbeeStatus>(ds);
+
+ registerType<TxData>(ds);
+ registerType<RxData>(ds);
+ registerType<Mark>(ds);
+ registerType<XbeeConfig>(ds);
+ registerType<EnergyScanData>(ds);
+}
diff --git a/src/tests/drivers/xbee/logdecoder/Makefile b/src/tests/drivers/xbee/logdecoder/Makefile
new file mode 100644
index 000000000..47258b629
--- /dev/null
+++ b/src/tests/drivers/xbee/logdecoder/Makefile
@@ -0,0 +1,13 @@
+BASE := ../../../../../
+
+all:
+ g++ -std=c++17 -O2 -o logdecoder logdecoder.cpp \
+ -DCOMPILE_FOR_X86 \
+ $(BASE)libs/tscpp/stream.cpp \
+ -I$(BASE)src/shared \
+ -I$(BASE)src/tests \
+ -I$(BASE)libs \
+ -I$(BASE)libs/miosix-kernel/miosix
+
+clean:
+ rm logdecoder
diff --git a/src/tests/drivers/xbee/logdecoder/logdecoder.cpp b/src/tests/drivers/xbee/logdecoder/logdecoder.cpp
new file mode 100644
index 000000000..3f1f725dd
--- /dev/null
+++ b/src/tests/drivers/xbee/logdecoder/logdecoder.cpp
@@ -0,0 +1,168 @@
+/***************************************************************************
+ * Copyright (C) 2018 by Terraneo Federico *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * As a special exception, if other files instantiate templates or use *
+ * macros or inline functions from this file, or you compile this file *
+ * and link it with other works to produce a work based on this file, *
+ * this file does not by itself cause the resulting work to be covered *
+ * by the GNU General Public License. However the source code for this *
+ * file must still be made available in accordance with the GNU General *
+ * Public License. This exception does not invalidate any other reasons *
+ * why a work based on this file might be covered by the GNU General *
+ * Public License. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see <http://www.gnu.org/licenses/> *
+ ***************************************************************************/
+
+/*
+ * This is a stub program for the program that will decode the logged data.
+ * Fill in the TODO to make it work.
+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <tscpp/stream.h>
+
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+#include "LogTypes.h"
+
+using namespace std;
+using namespace tscpp;
+
+namespace fs = filesystem;
+
+void showHelp(string cmdName)
+{
+ std::cerr << "Usage: " << cmdName
+ << " {-a [logs_diretory] | <log_file_path> | -h}"
+ << "Options:\n"
+ << "\t-h,--help\t\tShow help message\n"
+ << "\t-a,--all [dir=\".\"] Deserialize all logs in the provided directory\n"
+ << std::endl;
+}
+
+vector<fs::path> listLogFiles(fs::path dir)
+{
+ vector<fs::path> out;
+ for (const auto& entry : fs::directory_iterator(dir))
+ {
+ if (entry.exists() && entry.is_regular_file())
+ {
+ if (entry.path().extension() == ".dat")
+ {
+ out.push_back(entry.path());
+ }
+ }
+ }
+ return out;
+}
+
+bool deserialize(fs::path log_path)
+{
+ cout << "Deserializing " << log_path << ".dat...\n";
+
+ // remove extension
+ log_path.replace_extension("");
+ Deserializer d(log_path);
+ registerTypes(d);
+
+ return d.deserialize();
+}
+
+bool deserializeAll(fs::path dir = ".")
+{
+ vector<fs::path> logs = listLogFiles(dir);
+ for (auto log : logs)
+ {
+ if (!deserialize(log.string()))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int main(int argc, char* argv[])
+{
+ if (argc < 2)
+ {
+ showHelp(string(argv[0]));
+ return 1;
+ }
+
+ bool success = false;
+ string arg1 = string(argv[1]);
+ if (arg1 == "-h" || arg1 == "--help")
+ {
+ showHelp(string(argv[0]));
+ return 0;
+ }
+
+ if (arg1 == "-a" || arg1 == "--all")
+ {
+ fs::path dir = ".";
+ if (argc == 3)
+ {
+ string arg2 = string(argv[2]);
+ fs::directory_entry entry(arg2);
+ if (entry.exists() && entry.is_directory())
+ {
+ dir = arg2;
+ }
+ else
+ {
+ cout << "Second argument after '-a' or '--all' must be a "
+ "directory\n";
+ showHelp(string(argv[0]));
+ return 1;
+ }
+ }
+ cout << "Deserializing all logs...\n";
+ success = deserializeAll(dir);
+ }
+ else if (arg1[0] == '-')
+ {
+ cerr << "Unknown option\n";
+ return 1;
+ }
+ else
+ {
+ fs::directory_entry entry(arg1);
+ if (entry.exists() && entry.is_regular_file())
+ {
+ success = deserialize(arg1);
+ }
+ else
+ {
+ showHelp(string(argv[0]));
+ return 1;
+ }
+ }
+
+ if (success)
+ {
+ cout << "Deserialization completed successfully.\n";
+ }
+ else
+ {
+ cout << "Deserialization ended with errors.\n";
+ }
+}
diff --git a/src/tests/drivers/xbee/test-xbee-bidir.cpp b/src/tests/drivers/xbee/test-xbee-bidir.cpp
new file mode 100644
index 000000000..c8abcb445
--- /dev/null
+++ b/src/tests/drivers/xbee/test-xbee-bidir.cpp
@@ -0,0 +1,267 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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 RUN_SENDER
+#define RUN_SENDER true
+#endif
+#ifndef RUN_RECEIVER
+#define RUN_RECEIVER true
+#endif
+
+#include <miosix.h>
+
+#include <cstdio>
+#include <stdexcept>
+
+#include "ActiveObject.h"
+#include "XbeeTransceiver.h"
+#include "drivers/Xbee/APIFramesLog.h"
+#include "drivers/Xbee/ATCommands.h"
+#include "drivers/Xbee/Xbee.h"
+#include "drivers/interrupt/external_interrupts.h"
+#include "logger/Logger.h"
+
+using namespace miosix;
+
+#ifdef _BOARD_STM32F429ZI_SKYWARD_DEATHST_X
+#include "interfaces-impl/hwmapping.h"
+using GpioMiso = miosix::interfaces::spi2::miso;
+using GpioMosi = miosix::interfaces::spi2::mosi;
+using GpioSck = miosix::interfaces::spi2::sck;
+
+using GpioCS = xbee::cs;
+using GpioATTN = xbee::attn;
+using GpioRST = xbee::reset;
+
+using GpioLedLog = Gpio<GPIOG_BASE, 2>;
+
+#define XBEE_SPI SPI2
+#else
+using GpioMiso = Gpio<GPIOB_BASE, 4>;
+using GpioMosi = Gpio<GPIOA_BASE, 7>;
+using GpioSck = Gpio<GPIOA_BASE, 5>;
+
+using GpioCS = Gpio<GPIOC_BASE, 1>;
+using GpioATTN = Gpio<GPIOE_BASE, 5>;
+using GpioRST = Gpio<GPIOE_BASE, 6>;
+
+using GpioLedLog = Gpio<GPIOC_BASE, 13>;
+#define XBEE_SPI SPI1
+#endif
+
+using GpioUserBtn = Gpio<GPIOA_BASE, 0>;
+
+Xbee::Xbee* xbee_driver = nullptr;
+Logger& logger = Logger::instance();
+
+#ifdef _BOARD_STM32F429ZI_SKYWARD_DEATHST_X
+void __attribute__((used)) EXTI10_IRQHandlerImpl()
+#else
+void __attribute__((used)) EXTI5_IRQHandlerImpl()
+#endif
+{
+ if (xbee_driver != nullptr)
+ {
+ xbee_driver->handleATTNInterrupt();
+ }
+}
+
+int getUserBtnValue()
+{
+#ifdef _BOARD_STM32F429ZI_SKYWARD_DEATHST_X
+ return 0;
+#else
+ return GpioUserBtn::value();
+#endif
+}
+
+void configure()
+{
+#ifndef _BOARD_STM32F429ZI_SKYWARD_DEATHST_X
+ {
+ FastInterruptDisableLock dLock;
+
+ RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
+
+ // Set SPI pins to correct alternate mode
+ GpioSck::mode(Mode::ALTERNATE);
+ GpioMiso::mode(Mode::ALTERNATE);
+ GpioMosi::mode(Mode::ALTERNATE);
+
+ GpioSck::alternateFunction(5);
+ GpioMiso::alternateFunction(5);
+ GpioMosi::alternateFunction(5);
+
+ GpioATTN::mode(Mode::INPUT);
+
+ GpioLedLog::mode(Mode::OUTPUT);
+
+ GpioUserBtn::mode(Mode::INPUT_PULL_DOWN);
+
+ GpioCS::mode(Mode::OUTPUT);
+ }
+
+ GpioCS::high();
+ GpioLedLog::low();
+#endif
+
+#ifdef _BOARD_STM32F429ZI_SKYWARD_DEATHST_X
+ enableExternalInterrupt(GPIOF_BASE, 10, InterruptTrigger::FALLING_EDGE);
+#else
+ enableExternalInterrupt(GPIOE_BASE, 5, InterruptTrigger::FALLING_EDGE);
+#endif
+}
+
+void setupXbee(XbeeConfig config)
+{
+ if (xbee_driver)
+ {
+ if (config.data_rate_80k)
+ {
+ if (!Xbee::setDataRate(*xbee_driver, true))
+ {
+ TRACE("[main] Error setting xbee_driver data rate!\n");
+ }
+ }
+
+ if (!config.freq_hop)
+ {
+ if (!Xbee::disableFrequencyHopping(*xbee_driver))
+ {
+ TRACE("[main] Error disabling frequency hop!\n");
+ }
+ }
+ }
+}
+
+int main()
+{
+ XbeeConfig config;
+ config.data_rate_80k = true;
+ config.freq_hop = true;
+
+ config.packet_size = 256;
+ config.send_interval = 333;
+ config.tx_enabled = RUN_SENDER;
+ config.timestamp = getTick();
+
+ configure();
+
+ int filenum;
+ try
+ {
+ filenum = logger.start();
+
+ printf("\nLog file opened! (%s)\n\n",
+ logger.getFileName(filenum).c_str());
+ }
+ catch (const std::runtime_error& err)
+ {
+ GpioLedLog::high();
+ printf("\n!!!!!!Error opening log file!!!!!!!\n\n");
+ }
+
+ logger.log(config);
+
+ SPIBus spi_bus(XBEE_SPI);
+ SPIBusConfig cfg{};
+ cfg.clock_div = SPIClockDivider::DIV16;
+
+ GpioPin cs = GpioCS::getPin();
+ GpioPin attn = GpioATTN::getPin();
+ GpioPin rst = GpioRST::getPin();
+
+ xbee_driver = new Xbee::Xbee(spi_bus, cfg, cs, attn, rst);
+
+ setupXbee(config);
+
+ // RandSendInterval intv(333, 1500);
+ ConstSendInterval intv(config.send_interval);
+ XbeeTransceiver* trans =
+ new XbeeTransceiver(*xbee_driver, logger, intv, 256, 333);
+ if (!config.tx_enabled)
+ {
+ trans->disableSender();
+ }
+ if (!RUN_RECEIVER)
+ {
+ trans->disableReceiver();
+ }
+ trans->start();
+
+ while (getUserBtnValue() == 0)
+ {
+ long long loop_start = getTick();
+
+ DataRateResult res_rcv = trans->getReceiver().getDataRate();
+ DataRateResult res_snd = trans->getSender().getDataRate();
+
+ TxData txd = trans->getSender().getTxData();
+ RxData rxd = trans->getReceiver().getRxData();
+
+ logger.log(xbee_driver->getStatus());
+ logger.log(logger.getLogStats());
+
+ long long tick = getTick();
+ unsigned int h = tick / (1000 * 3600);
+ unsigned int m = (tick - h * 1000 * 3600) / (1000 * 60);
+ float s = (tick - h * 1000 * 3600 - m * 1000 * 60) / 1000.0f;
+
+ printf("%02u:%02u:%06.3f\n", h, m, s);
+ if (RUN_SENDER)
+ {
+ printf("SND: int: %d, cnt: %d, tts: %u ms, pps: %.1f, fail: % d\n ",
+ txd.time_since_last_send,
+ txd.tx_success_counter + txd.tx_fail_counter,
+ txd.time_to_send, res_snd.packets_per_second,
+ txd.tx_fail_counter);
+ }
+ if (RUN_RECEIVER)
+ {
+ printf(
+ "RCV: cnt: %d, last_rx: %lld ms, RSSI: %d, dr: %.0f, pps: "
+ "%.1f,"
+ " pl: %.0f%%, lcnt: %u, fail: %u\n",
+ rxd.rcv_count, rxd.last_packet_timestamp, rxd.RSSI,
+ res_rcv.data_rate, res_rcv.packets_per_second,
+ res_rcv.packet_loss * 100, rxd.packets_lost, rxd.rcv_errors);
+ }
+ printf("\n");
+
+ Thread::sleepUntil(loop_start + 1000);
+ }
+
+ trans->stop();
+ delete trans;
+ delete xbee_driver;
+ logger.stop();
+ printf("Log closed.\n");
+
+ for (;;)
+ {
+ GpioLedLog::high();
+ Thread::sleep(50);
+ GpioLedLog::low();
+ Thread::sleep(5000);
+ }
+}
\ No newline at end of file
diff --git a/src/tests/drivers/xbee/test-xbee-gui.cpp b/src/tests/drivers/xbee/test-xbee-gui.cpp
new file mode 100644
index 000000000..60019fa0c
--- /dev/null
+++ b/src/tests/drivers/xbee/test-xbee-gui.cpp
@@ -0,0 +1,368 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#include <miosix.h>
+#include <mxgui/display.h>
+
+#include <array>
+#include <cstdio>
+#include <functional>
+#include <stdexcept>
+
+#include "ActiveObject.h"
+#include "Mark.h"
+#include "XbeeTransceiver.h"
+#include "drivers/Xbee/APIFramesLog.h"
+#include "drivers/Xbee/ATCommands.h"
+#include "drivers/Xbee/Xbee.h"
+#include "drivers/interrupt/external_interrupts.h"
+#include "gui/XbeeGui.h"
+#include "logger/Logger.h"
+#include "utils/ButtonHandler.h"
+
+using namespace miosix;
+using namespace mxgui;
+using namespace std::placeholders;
+
+using std::array;
+using std::bind;
+using std::to_string;
+
+/// Pin definitions
+using GpioMiso = Gpio<GPIOB_BASE, 4>;
+using GpioMosi = Gpio<GPIOA_BASE, 7>;
+using GpioSck = Gpio<GPIOA_BASE, 5>;
+
+using GpioCS = Gpio<GPIOC_BASE, 1>;
+using GpioATTN = Gpio<GPIOE_BASE, 5>;
+using GpioRST = Gpio<GPIOE_BASE, 6>;
+
+using GpioUserBtn = Gpio<GPIOA_BASE, 0>;
+using GpioLedLog = Gpio<GPIOC_BASE, 13>;
+
+// Forward dec
+void onStartButtonClick(View* btn, Interaction action);
+void onEnergyButtonClick(View* btn, Interaction action);
+void onMarkButtonClick(View* btn, Interaction action);
+void onStopButtonClick(View* btn, Interaction action);
+void startTransceiver(XbeeConfig config);
+void setupXbee(XbeeConfig config);
+void configure();
+
+// Global variables
+Logger& logger = Logger::instance();
+Xbee::Xbee* xbee = nullptr;
+ConstSendInterval snd_int{0};
+XbeeTransceiver* trans = nullptr;
+XbeeGUI* gui;
+ButtonHandler<GpioUserBtn>* btn_handler;
+
+unsigned int mark_counter = 1;
+
+/**
+ * @brief Activeobject to perform an energy detect scan as frequently as
+ * possible
+ */
+class EnergyScanner : public ActiveObject
+{
+protected:
+ void run() override
+ {
+ while (!shouldStop())
+ {
+ Xbee::ATCommandResponseFrame response;
+
+ uint8_t duration = 0xFF;
+
+ if (xbee->sendATCommand("ED", &response, &duration, 1, 1000) &&
+ response.getCommandDataLength() == 30)
+ {
+ array<int, 30> scan;
+
+ for (uint16_t i = 0; i < response.getCommandDataLength(); i++)
+ {
+ scan[i] = -(int)(*(response.getCommandDataPointer() + i));
+ }
+
+ gui->screen_energy.updateScan(scan);
+
+ EnergyScanData data{getTick(), scan};
+ logger.log(data);
+ }
+ }
+ }
+} energy_scanner;
+
+int main()
+{
+ // Hardware
+ configure();
+
+ // SD
+ try
+ {
+ logger.start();
+ printf("\nLog file opened! (%s)\n\n",
+ logger.getFileName(logger.getLogNumber()).c_str());
+ }
+ catch (const std::runtime_error& err)
+ {
+ GpioLedLog::high();
+ printf("\n!!!!!!Error opening log file!!!!!!!\n\n");
+ }
+
+ // XBee
+ SPIBus spi_bus(SPI1);
+ SPIBusConfig cfg{};
+ cfg.clock_div = SPIClockDivider::DIV16;
+
+ GpioPin cs = GpioCS::getPin();
+ GpioPin attn = GpioATTN::getPin();
+ GpioPin rst = GpioRST::getPin();
+
+ xbee = new Xbee::Xbee(spi_bus, cfg, cs, attn, rst);
+
+ // GUI
+ gui = new XbeeGUI();
+
+ btn_handler = new ButtonHandler<GpioUserBtn>(
+ 0, bind(&ScreenManager::onButtonPress, &gui->screen_manager, _2));
+
+ btn_handler->start();
+
+ gui->screen_config.btn_start.addOnInteractionListener(onStartButtonClick);
+ gui->screen_config.btn_energy.addOnInteractionListener(onEnergyButtonClick);
+
+ gui->screen_status.btn_stop.addOnInteractionListener(onStopButtonClick);
+ gui->screen_status.btn_mark.addOnInteractionListener(onMarkButtonClick);
+
+ gui->screen_energy.btn_stop.addOnInteractionListener(onStopButtonClick);
+ gui->screen_energy.btn_mark.addOnInteractionListener(onMarkButtonClick);
+ gui->screen_energy.btn_reset.addOnInteractionListener(
+ [&](View* d, Interaction action) {
+ UNUSED(d);
+ if (action == Interaction::CLICK)
+ gui->screen_energy.resetStats();
+ });
+
+ gui->screen_end.tv_f.addOnInteractionListener(
+ [&](View* d, Interaction action) {
+ UNUSED(d);
+ if (action == Interaction::CLICK)
+ gui->screen_manager.showScreen(XbeeGUI::SCREEN_RESPECT);
+ });
+
+ gui->screen_end.tv_reset.addOnInteractionListener(
+ [&](View* d, Interaction action) {
+ UNUSED(d);
+ if (action == Interaction::CLICK)
+ miosix::reboot();
+ });
+
+ // Main loop: updates the information in the GUI
+ for (;;)
+ {
+ long long start = getTick();
+ // Update display values
+ switch (gui->screen_manager.getScreen())
+ {
+ case XbeeGUI::SCREEN_CONFIG:
+ gui->screen_config.updateLogStatus(logger);
+ break;
+ case XbeeGUI::SCREEN_STATUS:
+ if (trans && xbee)
+ {
+ gui->screen_status.updateXbeeStatus(
+ trans->getReceiver().getDataRate(),
+ trans->getSender().getDataRate(),
+ trans->getSender().getTxData(),
+ trans->getReceiver().getRxData(), xbee->getStatus());
+
+ logger.log(xbee->getStatus());
+ }
+
+ gui->screen_status.updateLogStatus(logger);
+ break;
+ case XbeeGUI::SCREEN_ENERGYSCAN:
+ gui->screen_energy.updateLogStatus(logger);
+ break;
+ default:
+ break;
+ }
+
+ logger.log(logger.getLogStats());
+ Thread::sleepUntil(start + 500);
+ }
+}
+
+void onStartButtonClick(View* btn, Interaction action)
+{
+ UNUSED(btn);
+ if (action == Interaction::CLICK)
+ {
+
+ XbeeConfig cfg = gui->screen_config.config;
+ cfg.timestamp = getTick();
+ logger.log(cfg);
+
+ gui->screen_config.btn_start.setText("Starting...");
+
+ gui->screen_status.updateConfig(cfg);
+
+ setupXbee(cfg);
+ startTransceiver(cfg);
+
+ // Show status screen
+ gui->screen_manager.showScreen(XbeeGUI::SCREEN_STATUS);
+ }
+}
+
+void onStopButtonClick(View* btn, Interaction action)
+{
+ if (action == Interaction::LONG_CLICK)
+ {
+ TextView* tv_btn = dynamic_cast<TextView*>(btn);
+
+ if (tv_btn)
+ {
+ tv_btn->setText("Stopping...");
+ }
+
+ if (trans)
+ {
+ trans->stop();
+ }
+
+ if (energy_scanner.isRunning())
+ {
+ energy_scanner.stop();
+ }
+
+ logger.stop();
+
+ gui->screen_manager.showScreen(XbeeGUI::SCREEN_END);
+ }
+}
+
+void onMarkButtonClick(View* btn, Interaction action)
+{
+ UNUSED(btn);
+ if (action == Interaction::CLICK)
+ {
+ Mark m{getTick(), mark_counter++};
+ logger.log(m);
+
+ TextView* tv_btn = dynamic_cast<TextView*>(btn);
+ if (tv_btn)
+ {
+ tv_btn->setText("Mark Log (" + to_string(mark_counter) + ")");
+ }
+ }
+}
+
+void onEnergyButtonClick(View* btn, Interaction action)
+{
+ UNUSED(btn);
+ if (action == Interaction::CLICK)
+ {
+ energy_scanner.start();
+ gui->screen_manager.showScreen(XbeeGUI::SCREEN_ENERGYSCAN);
+ }
+}
+
+void startTransceiver(XbeeConfig config)
+{
+ snd_int.interval = config.send_interval;
+ trans = new XbeeTransceiver(*xbee, logger, snd_int, config.packet_size,
+ config.send_interval);
+
+ if (!config.tx_enabled)
+ {
+ trans->disableSender();
+ }
+
+ trans->start();
+}
+
+void setupXbee(XbeeConfig config)
+{
+ if (config.data_rate_80k)
+ {
+ if (!Xbee::setDataRate(*xbee, true))
+ {
+ gui->screen_status.tv_cfg_data_rate.setBackgroundColor(mxgui::red);
+ TRACE("[main] Error setting xbee data rate!\n");
+ }
+ }
+
+ if (!config.freq_hop)
+ {
+ if (!Xbee::disableFrequencyHopping(*xbee))
+ {
+ gui->screen_status.tv_cfg_freq_hop.setBackgroundColor(mxgui::red);
+ TRACE("[main] Error disabling frequency hop!\n");
+ }
+ }
+}
+
+void configure()
+{
+ {
+ FastInterruptDisableLock dLock;
+
+ // Enable SPI5 and TIM5 peripheral clocks
+ RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
+
+ // Set SPI pins to correct alternate mode
+ GpioSck::mode(Mode::ALTERNATE);
+ GpioMiso::mode(Mode::ALTERNATE);
+ GpioMosi::mode(Mode::ALTERNATE);
+
+ GpioSck::alternateFunction(5);
+ GpioMiso::alternateFunction(5);
+ GpioMosi::alternateFunction(5);
+
+ GpioATTN::mode(Mode::INPUT_PULL_UP);
+
+ GpioLedLog::mode(Mode::OUTPUT);
+ GpioUserBtn::mode(Mode::INPUT_PULL_DOWN);
+
+ // Set chip select pin to OUTPUT
+ GpioCS::mode(Mode::OUTPUT);
+ }
+
+ // Chip select starts high (not asserted)
+ GpioCS::high();
+ GpioLedLog::low();
+
+ // Enable rising-edge interrupt detection on PA2
+ enableExternalInterrupt(GPIOE_BASE, 5, InterruptTrigger::FALLING_EDGE);
+}
+
+void __attribute__((used)) EXTI5_IRQHandlerImpl()
+{
+ if (xbee)
+ {
+ xbee->handleATTNInterrupt();
+ }
+}
\ No newline at end of file
diff --git a/src/tests/drivers/xbee/test-xbee-rcv.cpp b/src/tests/drivers/xbee/test-xbee-rcv.cpp
new file mode 100644
index 000000000..d542ddf69
--- /dev/null
+++ b/src/tests/drivers/xbee/test-xbee-rcv.cpp
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#define RUN_SENDER false
+
+#include "test-xbee-bidir.cpp"
\ No newline at end of file
diff --git a/src/tests/drivers/xbee/test-xbee-snd.cpp b/src/tests/drivers/xbee/test-xbee-snd.cpp
new file mode 100644
index 000000000..7c0f3508c
--- /dev/null
+++ b/src/tests/drivers/xbee/test-xbee-snd.cpp
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2021 Skyward Experimental Rocketry
+ * Authors: Luca Erbetta (luca.erbetta@skywarder.eu)
+ *
+ * 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.
+ */
+
+#define RUN_RECEIVER false
+
+#include "test-xbee-bidir.cpp"
diff --git a/src/tests/test-hsm.cpp b/src/tests/test-hsm.cpp
index 8495f86f8..2fa268716 100644
--- a/src/tests/test-hsm.cpp
+++ b/src/tests/test-hsm.cpp
@@ -32,7 +32,7 @@ using namespace miosix;
#define TOPIC_TEST 1
-#define CHECK_INIT() bool test_value = false
+#define CHECK_INIT() bool test_value = false; (void)test_value
#define CHECK_STATE(HSM, SIGNAL, STATE) do{ \
cout << "------------------------------" << endl; \
--
GitLab