diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 00000000..3a37aef0
Binary files /dev/null and b/.DS_Store differ
diff --git a/.gitignore b/.gitignore
index fa8bdb96..623dea42 100644
--- a/.gitignore
+++ b/.gitignore
@@ -111,3 +111,5 @@ docs/
# Google protocol buffers
*.pb
/.balena/
+
+following/min_app.py
diff --git a/archived/.DS_Store b/archived/.DS_Store
new file mode 100644
index 00000000..c5234abc
Binary files /dev/null and b/archived/.DS_Store differ
diff --git a/common/.DS_Store b/common/.DS_Store
new file mode 100644
index 00000000..6cc878a3
Binary files /dev/null and b/common/.DS_Store differ
diff --git a/common/byodr/utils/ipc.py b/common/byodr/utils/ipc.py
index 9812bbe1..35d93740 100644
--- a/common/byodr/utils/ipc.py
+++ b/common/byodr/utils/ipc.py
@@ -38,6 +38,12 @@ def send_string(sender, val, flags=0):
class JSONPublisher(object):
+ """
+ Attributes:
+ hwm (int): The high-water mark (queue size) for the publisher socket.
+ clean_start (bool): If True, removes existing IPC socket file before binding.
+ """
+
def __init__(self, url, topic="", hwm=1, clean_start=True):
if clean_start and url.startswith("ipc://") and os.path.exists(url[6:]):
os.remove(url[6:])
@@ -48,6 +54,7 @@ def __init__(self, url, topic="", hwm=1, clean_start=True):
self._topic = topic
def publish(self, data, topic=None):
+ """Removes any key-value pair where the value is `None`"""
_topic = self._topic if topic is None else topic
if data is not None:
data = dict((k, v) for k, v in data.items() if v is not None)
@@ -79,6 +86,11 @@ def publish(self, _img, topic=None):
class JSONReceiver(object):
def __init__(self, url, topic=b"", hwm=1, receive_timeout_ms=2, pop=False):
+ """
+ Args:
+ receive_timeout_ms (int): The timeout for receiving messages (optional, default is 2 ms).
+ pop (bool): If True, clears the queue after a message is retrieved (optional, default is False).
+ """
subscriber = zmq.Context().socket(zmq.SUB)
subscriber.set_hwm(hwm)
subscriber.setsockopt(zmq.RCVTIMEO, receive_timeout_ms)
@@ -100,12 +112,20 @@ def consume(self):
pass
def get(self):
+ """
+ Retrieves the latest message or the entire queue.
+
+ Returns:
+ The latest message if `hwm` is 1, otherwise the entire queue.
+ Clears the queue if `pop` is True.
+ """
_view = self._queue[0] if (self._queue and self._unpack) else list(self._queue) if self._queue else None
if self._pop:
self._queue.clear()
return _view
def peek(self):
+ """Peeks at the latest message without removing it from the queue."""
return self._queue[0] if self._queue else None
@@ -129,6 +149,12 @@ def quit(self):
self._quit_event.set()
def run(self):
+ """
+ Continuously consumes messages from all receivers.
+
+ This method runs in a loop until the quit event is set, consuming messages
+ from all receivers and sleeping for a specified duration between polls.
+ """
while not self._quit_event.is_set():
# Empty the receiver queues to not block upstream senders.
list(map(lambda receiver: receiver.consume(), self._receivers))
diff --git a/docker-compose.yml b/docker-compose.yml
index c84fe675..c2fc033a 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -61,18 +61,6 @@ services:
volumes:
- volume_ftpd_config:/etc/pureftpd:rw
- volume_byodr_sessions:/home/ftpuser:rw
- rosnode:
- cpuset: '0'
- build:
- context: .
- dockerfile: rosnode/Dockerfile
- restart: always
- command: ['python3', 'app.py', '--name', 'rosnode']
- network_mode: host
- stop_signal: SIGKILL
- volumes:
- - volume_byodr_sockets:/byodr:rw
- - volume_byodr_config:/config:ro
mongodb:
cpuset: '0'
build:
@@ -116,7 +104,7 @@ services:
- volume_byodr_sockets:/byodr:rw
- volume_byodr_config:/config:rw
pilot:
- cpuset: '2'
+ cpuset: '1'
build:
context: .
dockerfile: pilot/Dockerfile
@@ -145,3 +133,15 @@ services:
volumes:
- volume_byodr_sockets:/byodr:rw
- volume_byodr_config:/config:ro
+ following:
+ cpuset: '3,2'
+ build:
+ context: .
+ dockerfile: following/Dockerfile
+ restart: always
+ privileged: true
+ network_mode: host
+ stop_signal: SIGKILL
+ volumes:
+ - volume_byodr_sockets:/byodr:rw
+ - volume_byodr_config:/config:rw
diff --git a/docker/bionic-torch1.10-py36-cuda10.2.dockerfile b/docker/bionic-torch1.10-py36-cuda10.2.dockerfile
new file mode 100644
index 00000000..c2012214
--- /dev/null
+++ b/docker/bionic-torch1.10-py36-cuda10.2.dockerfile
@@ -0,0 +1,71 @@
+# This pytorch version is strictly built with cp36. I did try to have a cp310 version
+# [torch-1.10.0-cp39-cp39-manylinux2014_aarch64.whl](https://files.pythonhosted.org/packages/03/f6/67e0ef29a03fd1cf585bdec03eb3aaf9f00498474f5c7b59f83d9779a7f1/torch-1.10.0-cp39-cp39-manylinux2014_aarch64.whl) but it didn't work with CUDA.
+
+# Some links I used while making this image
+# find supported versions between pytorch and torchvision => https://catalog.ngc.nvidia.com/orgs/nvidia/containers/l4t-pytorch
+# https://forums.developer.nvidia.com/t/pytorch-for-jetson/72048
+# https://developer.nvidia.com/embedded/jetson-linux-archive
+# Supported OS to run CUDA => https://developer.nvidia.com/cuda-10.2-download-archive?target_os=Linux&target_arch=x86_64&target_distro=Ubuntu (most of the other OS do not have optimized version with Balena os)
+# Download wheel version for pytorch (in case of updating) => https://download.pytorch.org/whl/torch/
+# find optimized builds made for Nano on Balena => Nvidia Jetson Nano SD-CARD in https://docs.balena.io/reference/base-images/base-images-ref/
+# numpy build https://gitlab.com/pdboef/tensorrt-balena/-/blob/master/Dockerfile.tensorrt and https://github.com/zrzka/python-wheel-aarch64/releases
+# for tensorflow https://gitlab.com/pdboef/tensorrt-balena/-/tree/master from https://forums.balena.io/t/jetson-nano-image-with-tensorrt-for-python-projects/19272
+# main optimized blena docker image => https://github.com/balena-io-library/base-images/blob/master/balena-base-images/device-base/jetson-nano/ubuntu/bionic/run/Dockerfile
+# the python optimized image =>https://github.com/balena-io-library/base-images/blob/master/balena-base-images/python/jetson-nano/ubuntu/bionic/3.9.16/run/Dockerfile
+
+#Images to switch between
+# FROM balenalib/jetson-nano-ubuntu-python:bionic
+# FROM balenalib/jetson-nano-ubuntu-python:3.6-bionic-run
+
+# Image name : jetson-nano-torch1.10-py36-cuda10.2
+# FROM balenalib/jetson-nano-ubuntu:bionic
+FROM balenalib/jetson-nano-ubuntu:bionic
+
+# Combine ENV statements and don't prompt with any configuration questions
+ENV DEBIAN_FRONTEND=noninteractive \
+ CUDA_HOME=/usr/local/cuda-10.2 \
+ UDEV=1
+
+# Update to 32.7 repository, add Universe repository, and install required packages
+RUN sed -i 's/r32.6 main/r32.7 main/g' /etc/apt/sources.list.d/nvidia.list && \
+ apt-get update -qq && \
+ apt-get install -y --no-install-recommends software-properties-common && \
+ add-apt-repository universe && \
+ apt-get install -y --no-install-recommends lbzip2 git wget unzip jq xorg tar python3 libegl1 binutils \
+ python3-gi python3-dev python3-gst-1.0 python3-pip \
+ nvidia-l4t-cuda nvidia-cuda libopenmpi-dev cuda-toolkit-10-2 \
+ cuda-samples-10-2 libcudnn8 libopenblas-base libomp-dev python3-zmq
+
+# Clean up in a separate RUN command to enforce your requirement
+RUN apt-get clean && \
+ rm -rf /var/lib/apt/lists/* /usr/local/cuda-10.2/doc
+
+RUN pip3 install --upgrade pip --quiet
+
+# Install BSP binaries for L4T 32.7.2 if they don't exist and perform cleanup
+RUN if [ ! -f jetson-210_linux_r32.7.2_aarch64.tbz2 ]; then \
+ wget -q https://developer.nvidia.com/embedded/l4t/r32_release_v7.2/t210/jetson-210_linux_r32.7.2_aarch64.tbz2; \
+ fi && \
+ if [ -f jetson-210_linux_r32.7.2_aarch64.tbz2 ]; then \
+ tar xf jetson-210_linux_r32.7.2_aarch64.tbz2 && \
+ cd Linux_for_Tegra && \
+ sed -i 's/config.tbz2\"/config.tbz2\" --exclude=etc\/hosts --exclude=etc\/hostname/g' apply_binaries.sh && \
+ sed -i 's/install --owner=root --group=root \"${QEMU_BIN}\" \"${L4T_ROOTFS_DIR}\/usr\/bin\/\"/#install --owner=root --group=root \"${QEMU_BIN}\" \"${L4T_ROOTFS_DIR}\/usr\/bin\/\"/g' nv_tegra/nv-apply-debs.sh && \
+ sed -i 's/chroot . \// /g' nv_tegra/nv-apply-debs.sh && \
+ ./apply_binaries.sh -r / --target-overlay && cd .. && \
+ rm -rf jetson-210_linux_r32.7.2_aarch64.tbz2 Linux_for_Tegra; \
+ fi && \
+ echo "/usr/lib/aarch64-linux-gnu/tegra" > /etc/ld.so.conf.d/nvidia-tegra.conf && ldconfig
+
+# PyTorch installation with quiet flag to suppress progress output
+ARG PYTORCH_URL=https://nvidia.box.com/shared/static/fjtbno0vpo676a25cgvuqc1wty0fkkg6.whl
+ARG PYTORCH_WHL=torch-1.10.0-cp36-cp36m-linux_aarch64.whl
+RUN wget -q --no-check-certificate ${PYTORCH_URL} -O ${PYTORCH_WHL} && \
+ pip3 install ${PYTORCH_WHL} --quiet && \
+ rm ${PYTORCH_WHL}
+
+# Numpy installation with quiet flag
+ARG WHL_URL_PREFIX=https://github.com/zrzka/python-wheel-aarch64/releases/download/jetson-nano-1.1
+RUN curl -LO ${WHL_URL_PREFIX}/numpy-1.16.4-cp36-cp36m-linux_aarch64.whl && \
+ python3 -m pip install numpy-1.16.4-cp36-cp36m-linux_aarch64.whl --quiet && \
+ rm numpy-1.16.4-cp36-cp36m-linux_aarch64.whl
\ No newline at end of file
diff --git a/docker/bionic-torch1.11-cp38-cuda10.2.Dockerfile b/docker/bionic-torch1.11-cp38-cuda10.2.Dockerfile
new file mode 100644
index 00000000..196f9997
--- /dev/null
+++ b/docker/bionic-torch1.11-cp38-cuda10.2.Dockerfile
@@ -0,0 +1,49 @@
+# Files inside yolo config directory will be referred to as /workspace/ultralytics/ultralytics/cfg/models/v8/
+# building command ==> docker buildx build --platform linux/arm64,linux/amd64 -t mwlvdev/jetson-nano-ubuntu:bionic-torch1.10-cp38-cuda10.2 . --push
+# Start from the base image
+FROM balenalib/jetson-nano-ubuntu:bionic
+
+# Environment variables for non-interactive installation and path configuration
+ENV DEBIAN_FRONTEND noninteractive
+ENV PATH="/workspace/ultralytics/venv/bin:$PATH"
+
+# Add Kitware GPG key to avoid errors on apt-get update
+RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1A127079A92F09ED
+
+# Add Kitware GPG key, update sources list for Nvidia, and install dependencies
+RUN apt-get update && \
+ # Update to 32.7 repository in case the base image is using 32.6
+ sed -i 's/r32.6 main/r32.7 main/g' /etc/apt/sources.list.d/nvidia.list && \
+ apt-get install -y --no-install-recommends cuda-toolkit-10-2 cuda-samples-10-2 libcudnn8 lbzip2 xorg wget tar libegl1 git \
+ python3.8 python3.8-venv python3.8-dev python3-pip \
+ libopenmpi-dev libomp-dev libopenblas-dev libblas-dev libeigen3-dev libcublas-dev && \
+ # Clean up
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
+
+# Download and install BSP binaries for L4T 32.7.2, and perform configurations
+RUN wget https://developer.nvidia.com/embedded/l4t/r32_release_v7.2/t210/jetson-210_linux_r32.7.2_aarch64.tbz2 && \
+ tar xf jetson-210_linux_r32.7.2_aarch64.tbz2 && \
+ cd Linux_for_Tegra && \
+ sed -i 's/config.tbz2\"/config.tbz2\" --exclude=etc\/hosts --exclude=etc\/hostname/g' apply_binaries.sh && \
+ sed -i 's/install --owner=root --group=root \"${QEMU_BIN}\" \"${L4T_ROOTFS_DIR}\/usr\/bin\/\"/#install --owner=root --group=root \"${QEMU_BIN}\" \"${L4T_ROOTFS_DIR}\/usr\/bin\/\"/g' nv_tegra/nv-apply-debs.sh && \
+ sed -i 's/chroot . \// /g' nv_tegra/nv-apply-debs.sh && \
+ ./apply_binaries.sh -r / --target-overlay && cd .. && \
+ rm -rf jetson-210_linux_r32.7.2_aarch64.tbz2 Linux_for_Tegra && \
+ echo "/usr/lib/aarch64-linux-gnu/tegra" > /etc/ld.so.conf.d/nvidia-tegra.conf && ldconfig
+
+# Setup the working environment
+WORKDIR /workspace
+
+# Clone the YOLOv8 repository and setup Python environment
+RUN git clone https://github.com/ultralytics/ultralytics.git && \
+ cd ultralytics && \
+ python3.8 -m venv venv && \
+ pip install -U pip wheel gdown && \
+ # Download pre-built PyTorch and TorchVision packages and install
+ gdown https://drive.google.com/uc?id=1hs9HM0XJ2LPFghcn7ZMOs5qu5HexPXwM && \
+ gdown https://drive.google.com/uc?id=1m0d8ruUY8RvCP9eVjZw4Nc8LAwM8yuGV && \
+ pip install torch-*.whl torchvision-*.whl && \
+ rm torch-*.whl torchvision-*.whl && \
+ # Install the Python package for YOLOv8
+ pip install .
\ No newline at end of file
diff --git a/docs/img/autopilot/ambiguous_corridor.png b/docs/img/autopilot/ambiguous_corridor.png
deleted file mode 100644
index d196c268..00000000
Binary files a/docs/img/autopilot/ambiguous_corridor.png and /dev/null differ
diff --git a/docs/img/autopilot/blocked_passage.png b/docs/img/autopilot/blocked_passage.png
deleted file mode 100644
index 20c39f09..00000000
Binary files a/docs/img/autopilot/blocked_passage.png and /dev/null differ
diff --git a/docs/img/autopilot/browser_navigationpoint_recognised1.jpg b/docs/img/autopilot/browser_navigationpoint_recognised1.jpg
deleted file mode 100644
index 9205c8f6..00000000
Binary files a/docs/img/autopilot/browser_navigationpoint_recognised1.jpg and /dev/null differ
diff --git a/docs/img/autopilot/browser_route_pause.jpg b/docs/img/autopilot/browser_route_pause.jpg
deleted file mode 100644
index e16079a8..00000000
Binary files a/docs/img/autopilot/browser_route_pause.jpg and /dev/null differ
diff --git a/docs/img/autopilot/browser_route_selector1.jpg b/docs/img/autopilot/browser_route_selector1.jpg
deleted file mode 100644
index 5b47eaab..00000000
Binary files a/docs/img/autopilot/browser_route_selector1.jpg and /dev/null differ
diff --git a/docs/img/autopilot/browser_route_start.jpg b/docs/img/autopilot/browser_route_start.jpg
deleted file mode 100644
index 8225fd68..00000000
Binary files a/docs/img/autopilot/browser_route_start.jpg and /dev/null differ
diff --git a/docs/img/autopilot/corridor_average.png b/docs/img/autopilot/corridor_average.png
deleted file mode 100644
index 5106960b..00000000
Binary files a/docs/img/autopilot/corridor_average.png and /dev/null differ
diff --git a/docs/img/autopilot/corridor_crossing.png b/docs/img/autopilot/corridor_crossing.png
deleted file mode 100644
index 65d8c610..00000000
Binary files a/docs/img/autopilot/corridor_crossing.png and /dev/null differ
diff --git a/docs/img/autopilot/corridor_merge.png b/docs/img/autopilot/corridor_merge.png
deleted file mode 100644
index 848f8260..00000000
Binary files a/docs/img/autopilot/corridor_merge.png and /dev/null differ
diff --git a/docs/img/autopilot/double_crossing.png b/docs/img/autopilot/double_crossing.png
deleted file mode 100644
index cb09dc94..00000000
Binary files a/docs/img/autopilot/double_crossing.png and /dev/null differ
diff --git a/docs/img/autopilot/filezilla_navigationpointfolder.jpg b/docs/img/autopilot/filezilla_navigationpointfolder.jpg
deleted file mode 100644
index ecf80367..00000000
Binary files a/docs/img/autopilot/filezilla_navigationpointfolder.jpg and /dev/null differ
diff --git a/docs/img/autopilot/filezilla_newroutes.jpg b/docs/img/autopilot/filezilla_newroutes.jpg
deleted file mode 100644
index 2cd31e7d..00000000
Binary files a/docs/img/autopilot/filezilla_newroutes.jpg and /dev/null differ
diff --git a/docs/img/autopilot/filezilla_robot_side.jpg b/docs/img/autopilot/filezilla_robot_side.jpg
deleted file mode 100644
index 538fdfc1..00000000
Binary files a/docs/img/autopilot/filezilla_robot_side.jpg and /dev/null differ
diff --git a/docs/img/autopilot/json_30_startrampup.jpg b/docs/img/autopilot/json_30_startrampup.jpg
deleted file mode 100644
index c6a6ad4f..00000000
Binary files a/docs/img/autopilot/json_30_startrampup.jpg and /dev/null differ
diff --git a/docs/img/autopilot/json_50_endrampup.jpg b/docs/img/autopilot/json_50_endrampup.jpg
deleted file mode 100644
index a6056c39..00000000
Binary files a/docs/img/autopilot/json_50_endrampup.jpg and /dev/null differ
diff --git a/docs/img/autopilot/json_80_stopbeforedown.jpg b/docs/img/autopilot/json_80_stopbeforedown.jpg
deleted file mode 100644
index ae48e430..00000000
Binary files a/docs/img/autopilot/json_80_stopbeforedown.jpg and /dev/null differ
diff --git a/docs/img/autopilot/labyrinth.png b/docs/img/autopilot/labyrinth.png
deleted file mode 100644
index af2e46f2..00000000
Binary files a/docs/img/autopilot/labyrinth.png and /dev/null differ
diff --git a/docs/img/autopilot/labyrinth2.png b/docs/img/autopilot/labyrinth2.png
deleted file mode 100644
index 942e2aef..00000000
Binary files a/docs/img/autopilot/labyrinth2.png and /dev/null differ
diff --git a/docs/img/autopilot/max_speed_zero.jpg b/docs/img/autopilot/max_speed_zero.jpg
deleted file mode 100644
index 9f686c7e..00000000
Binary files a/docs/img/autopilot/max_speed_zero.jpg and /dev/null differ
diff --git a/docs/img/autopilot/navigationpoint_choice.jpg b/docs/img/autopilot/navigationpoint_choice.jpg
deleted file mode 100644
index d430390c..00000000
Binary files a/docs/img/autopilot/navigationpoint_choice.jpg and /dev/null differ
diff --git a/docs/img/autopilot/navigationpointpictures.jpg b/docs/img/autopilot/navigationpointpictures.jpg
deleted file mode 100644
index 8fad0dba..00000000
Binary files a/docs/img/autopilot/navigationpointpictures.jpg and /dev/null differ
diff --git a/docs/img/autopilot/ros2_node_name_setting.jpg b/docs/img/autopilot/ros2_node_name_setting.jpg
deleted file mode 100644
index 876225ab..00000000
Binary files a/docs/img/autopilot/ros2_node_name_setting.jpg and /dev/null differ
diff --git a/docs/img/autopilot/rover_front.jpg b/docs/img/autopilot/rover_front.jpg
deleted file mode 100644
index 2108f416..00000000
Binary files a/docs/img/autopilot/rover_front.jpg and /dev/null differ
diff --git a/docs/img/autopilot/rover_frontOud.jpg b/docs/img/autopilot/rover_frontOud.jpg
deleted file mode 100644
index 9046c7d0..00000000
Binary files a/docs/img/autopilot/rover_frontOud.jpg and /dev/null differ
diff --git a/docs/img/autopilot/two_junctions.png b/docs/img/autopilot/two_junctions.png
deleted file mode 100644
index 22420018..00000000
Binary files a/docs/img/autopilot/two_junctions.png and /dev/null differ
diff --git a/docs/img/autopilot/uncertain.png b/docs/img/autopilot/uncertain.png
deleted file mode 100644
index 81397ae0..00000000
Binary files a/docs/img/autopilot/uncertain.png and /dev/null differ
diff --git a/docs/img/controller/cert_20lockiconclicked.jpg b/docs/img/controller/cert_20lockiconclicked.jpg
deleted file mode 100644
index bc6a346d..00000000
Binary files a/docs/img/controller/cert_20lockiconclicked.jpg and /dev/null differ
diff --git a/docs/img/controller/cert_25connectionnotsecureddetailsclicked.jpg b/docs/img/controller/cert_25connectionnotsecureddetailsclicked.jpg
deleted file mode 100644
index 46ad619d..00000000
Binary files a/docs/img/controller/cert_25connectionnotsecureddetailsclicked.jpg and /dev/null differ
diff --git a/docs/img/controller/cert_30moreinformationclicked.jpg b/docs/img/controller/cert_30moreinformationclicked.jpg
deleted file mode 100644
index a8595500..00000000
Binary files a/docs/img/controller/cert_30moreinformationclicked.jpg and /dev/null differ
diff --git a/docs/img/controller/cert_35viewcertificateclicked.jpg b/docs/img/controller/cert_35viewcertificateclicked.jpg
deleted file mode 100644
index 99704766..00000000
Binary files a/docs/img/controller/cert_35viewcertificateclicked.jpg and /dev/null differ
diff --git a/docs/img/controller/cert_40downloadcertificateclicked.jpg b/docs/img/controller/cert_40downloadcertificateclicked.jpg
deleted file mode 100644
index ed4a0501..00000000
Binary files a/docs/img/controller/cert_40downloadcertificateclicked.jpg and /dev/null differ
diff --git a/docs/img/controller/cert_50certificateopeninkeychain.jpg b/docs/img/controller/cert_50certificateopeninkeychain.jpg
deleted file mode 100644
index e4617eac..00000000
Binary files a/docs/img/controller/cert_50certificateopeninkeychain.jpg and /dev/null differ
diff --git a/docs/img/controller/cert_55trustopen.jpg b/docs/img/controller/cert_55trustopen.jpg
deleted file mode 100644
index 6dd8f0d3..00000000
Binary files a/docs/img/controller/cert_55trustopen.jpg and /dev/null differ
diff --git a/docs/img/controller/dash_center_autopilot.png b/docs/img/controller/dash_center_autopilot.png
deleted file mode 100644
index 2392e0b6..00000000
Binary files a/docs/img/controller/dash_center_autopilot.png and /dev/null differ
diff --git a/docs/img/controller/dash_center_black.png b/docs/img/controller/dash_center_black.png
deleted file mode 100644
index 94f6c103..00000000
Binary files a/docs/img/controller/dash_center_black.png and /dev/null differ
diff --git a/docs/img/controller/dash_center_blue.png b/docs/img/controller/dash_center_blue.png
deleted file mode 100644
index 7abca9f6..00000000
Binary files a/docs/img/controller/dash_center_blue.png and /dev/null differ
diff --git a/docs/img/controller/dash_center_red.png b/docs/img/controller/dash_center_red.png
deleted file mode 100644
index 2bc93189..00000000
Binary files a/docs/img/controller/dash_center_red.png and /dev/null differ
diff --git a/docs/img/controller/dash_center_teleop.png b/docs/img/controller/dash_center_teleop.png
deleted file mode 100644
index 852a5ce5..00000000
Binary files a/docs/img/controller/dash_center_teleop.png and /dev/null differ
diff --git a/docs/img/controller/msg_another_user_in_control.png b/docs/img/controller/msg_another_user_in_control.png
deleted file mode 100644
index ea22cc33..00000000
Binary files a/docs/img/controller/msg_another_user_in_control.png and /dev/null differ
diff --git a/docs/img/controller/msg_connection_lost.png b/docs/img/controller/msg_connection_lost.png
deleted file mode 100644
index 45ffd86d..00000000
Binary files a/docs/img/controller/msg_connection_lost.png and /dev/null differ
diff --git a/docs/img/controller/msg_controller_not_detected.png b/docs/img/controller/msg_controller_not_detected.png
deleted file mode 100644
index c98e418a..00000000
Binary files a/docs/img/controller/msg_controller_not_detected.png and /dev/null differ
diff --git a/docs/img/controller/ps4_autopilot.jpg b/docs/img/controller/ps4_autopilot.jpg
deleted file mode 100644
index 643a5523..00000000
Binary files a/docs/img/controller/ps4_autopilot.jpg and /dev/null differ
diff --git a/docs/img/controller/ps4_autopilot_overrule.jpg b/docs/img/controller/ps4_autopilot_overrule.jpg
deleted file mode 100644
index b241d13a..00000000
Binary files a/docs/img/controller/ps4_autopilot_overrule.jpg and /dev/null differ
diff --git a/docs/img/controller/ps4_camera.jpg b/docs/img/controller/ps4_camera.jpg
deleted file mode 100644
index 0f076889..00000000
Binary files a/docs/img/controller/ps4_camera.jpg and /dev/null differ
diff --git a/docs/img/controller/ps4_cameraOud.jpg b/docs/img/controller/ps4_cameraOud.jpg
deleted file mode 100644
index 25ac2821..00000000
Binary files a/docs/img/controller/ps4_cameraOud.jpg and /dev/null differ
diff --git a/docs/img/controller/ps4_forward_backward.jpg b/docs/img/controller/ps4_forward_backward.jpg
deleted file mode 100644
index 0f2c468a..00000000
Binary files a/docs/img/controller/ps4_forward_backward.jpg and /dev/null differ
diff --git a/docs/img/controller/ps4_forward_backwardOud.jpg b/docs/img/controller/ps4_forward_backwardOud.jpg
deleted file mode 100644
index 951a0d8a..00000000
Binary files a/docs/img/controller/ps4_forward_backwardOud.jpg and /dev/null differ
diff --git a/docs/img/controller/ps4_overview.jpg b/docs/img/controller/ps4_overview.jpg
deleted file mode 100644
index 1fd2a674..00000000
Binary files a/docs/img/controller/ps4_overview.jpg and /dev/null differ
diff --git a/docs/img/controller/ps4_reverse_gear.jpg b/docs/img/controller/ps4_reverse_gear.jpg
deleted file mode 100644
index 2e07268e..00000000
Binary files a/docs/img/controller/ps4_reverse_gear.jpg and /dev/null differ
diff --git a/docs/img/controller/ps4_teleop.jpg b/docs/img/controller/ps4_teleop.jpg
deleted file mode 100644
index 077e47ec..00000000
Binary files a/docs/img/controller/ps4_teleop.jpg and /dev/null differ
diff --git a/docs/img/controller/teleop_feedback.jpg b/docs/img/controller/teleop_feedback.jpg
deleted file mode 100644
index f2d5994c..00000000
Binary files a/docs/img/controller/teleop_feedback.jpg and /dev/null differ
diff --git a/docs/img/controller/turn_left.png b/docs/img/controller/turn_left.png
deleted file mode 100644
index e78c0a25..00000000
Binary files a/docs/img/controller/turn_left.png and /dev/null differ
diff --git a/docs/img/controller/turn_right.png b/docs/img/controller/turn_right.png
deleted file mode 100644
index 33eb247a..00000000
Binary files a/docs/img/controller/turn_right.png and /dev/null differ
diff --git a/docs/img/controller/turn_straight.png b/docs/img/controller/turn_straight.png
deleted file mode 100644
index 40257479..00000000
Binary files a/docs/img/controller/turn_straight.png and /dev/null differ
diff --git a/docs/img/controller/ui_menu_options.png b/docs/img/controller/ui_menu_options.png
deleted file mode 100644
index 779e8ae0..00000000
Binary files a/docs/img/controller/ui_menu_options.png and /dev/null differ
diff --git a/docs/img/controller/ui_overview.jpg b/docs/img/controller/ui_overview.jpg
deleted file mode 100644
index 9b211d9a..00000000
Binary files a/docs/img/controller/ui_overview.jpg and /dev/null differ
diff --git a/docs/img/controller/ui_overviewOud.jpg b/docs/img/controller/ui_overviewOud.jpg
deleted file mode 100644
index 552630b0..00000000
Binary files a/docs/img/controller/ui_overviewOud.jpg and /dev/null differ
diff --git a/docs/img/controller/wheel_black.png b/docs/img/controller/wheel_black.png
deleted file mode 100644
index e16a2cf9..00000000
Binary files a/docs/img/controller/wheel_black.png and /dev/null differ
diff --git a/docs/img/controller/wheel_blue_speed.png b/docs/img/controller/wheel_blue_speed.png
deleted file mode 100644
index 312bccdf..00000000
Binary files a/docs/img/controller/wheel_blue_speed.png and /dev/null differ
diff --git a/docs/img/controller/wheel_red.png b/docs/img/controller/wheel_red.png
deleted file mode 100644
index 4c8170cc..00000000
Binary files a/docs/img/controller/wheel_red.png and /dev/null differ
diff --git a/docs/img/controller/wheel_red_speed.png b/docs/img/controller/wheel_red_speed.png
deleted file mode 100644
index 3a281cc9..00000000
Binary files a/docs/img/controller/wheel_red_speed.png and /dev/null differ
diff --git a/docs/img/controller/xbox_white.png b/docs/img/controller/xbox_white.png
deleted file mode 100644
index 8277ae5d..00000000
Binary files a/docs/img/controller/xbox_white.png and /dev/null differ
diff --git a/docs/img/drawings/ZZ_Archief/img204400v09.jpg b/docs/img/drawings/ZZ_Archief/img204400v09.jpg
deleted file mode 100644
index a0e76cfd..00000000
Binary files a/docs/img/drawings/ZZ_Archief/img204400v09.jpg and /dev/null differ
diff --git a/docs/img/drawings/ZZ_Archief/img206000P1v018.jpg b/docs/img/drawings/ZZ_Archief/img206000P1v018.jpg
deleted file mode 100644
index 0dd18844..00000000
Binary files a/docs/img/drawings/ZZ_Archief/img206000P1v018.jpg and /dev/null differ
diff --git a/docs/img/drawings/ZZ_Archief/img206000P1v019.jpg b/docs/img/drawings/ZZ_Archief/img206000P1v019.jpg
deleted file mode 100644
index 50503949..00000000
Binary files a/docs/img/drawings/ZZ_Archief/img206000P1v019.jpg and /dev/null differ
diff --git a/docs/img/drawings/ZZ_Archief/img206000P2v018.jpg b/docs/img/drawings/ZZ_Archief/img206000P2v018.jpg
deleted file mode 100644
index 7450ef6b..00000000
Binary files a/docs/img/drawings/ZZ_Archief/img206000P2v018.jpg and /dev/null differ
diff --git a/docs/img/drawings/ZZ_Archief/img206000P2v019.jpg b/docs/img/drawings/ZZ_Archief/img206000P2v019.jpg
deleted file mode 100644
index 1b636467..00000000
Binary files a/docs/img/drawings/ZZ_Archief/img206000P2v019.jpg and /dev/null differ
diff --git a/docs/img/drawings/img204400v011.jpg b/docs/img/drawings/img204400v011.jpg
deleted file mode 100644
index 3cb6538e..00000000
Binary files a/docs/img/drawings/img204400v011.jpg and /dev/null differ
diff --git a/docs/img/drawings/img206012v021.jpg b/docs/img/drawings/img206012v021.jpg
deleted file mode 100644
index 991f6872..00000000
Binary files a/docs/img/drawings/img206012v021.jpg and /dev/null differ
diff --git a/docs/img/drawings/img206020v022.jpg b/docs/img/drawings/img206020v022.jpg
deleted file mode 100644
index 8aa6f184..00000000
Binary files a/docs/img/drawings/img206020v022.jpg and /dev/null differ
diff --git a/docs/img/drawings/img208000v07.jpg b/docs/img/drawings/img208000v07.jpg
deleted file mode 100644
index 3d037453..00000000
Binary files a/docs/img/drawings/img208000v07.jpg and /dev/null differ
diff --git a/docs/img/index/rover_garden.jpg b/docs/img/index/rover_garden.jpg
deleted file mode 100644
index d3a2aee0..00000000
Binary files a/docs/img/index/rover_garden.jpg and /dev/null differ
diff --git a/docs/img/instructions/img204110.jpg b/docs/img/instructions/img204110.jpg
deleted file mode 100644
index be7e6288..00000000
Binary files a/docs/img/instructions/img204110.jpg and /dev/null differ
diff --git a/docs/img/instructions/img204120.jpg b/docs/img/instructions/img204120.jpg
deleted file mode 100644
index 4e9700b1..00000000
Binary files a/docs/img/instructions/img204120.jpg and /dev/null differ
diff --git a/docs/img/instructions/img204215.jpg b/docs/img/instructions/img204215.jpg
deleted file mode 100644
index d2a0ec45..00000000
Binary files a/docs/img/instructions/img204215.jpg and /dev/null differ
diff --git a/docs/img/instructions/img204405.jpg b/docs/img/instructions/img204405.jpg
deleted file mode 100644
index 2faff37a..00000000
Binary files a/docs/img/instructions/img204405.jpg and /dev/null differ
diff --git a/docs/img/instructions/img204415.jpg b/docs/img/instructions/img204415.jpg
deleted file mode 100644
index e744e2c8..00000000
Binary files a/docs/img/instructions/img204415.jpg and /dev/null differ
diff --git a/docs/img/instructions/img204425.jpg b/docs/img/instructions/img204425.jpg
deleted file mode 100644
index c6ed06ca..00000000
Binary files a/docs/img/instructions/img204425.jpg and /dev/null differ
diff --git a/docs/img/instructions/img206120.jpg b/docs/img/instructions/img206120.jpg
deleted file mode 100644
index a1717ef7..00000000
Binary files a/docs/img/instructions/img206120.jpg and /dev/null differ
diff --git a/docs/img/instructions/img206230.jpg b/docs/img/instructions/img206230.jpg
deleted file mode 100644
index 192946c9..00000000
Binary files a/docs/img/instructions/img206230.jpg and /dev/null differ
diff --git a/docs/img/instructions/img206235.jpg b/docs/img/instructions/img206235.jpg
deleted file mode 100644
index b2f3c644..00000000
Binary files a/docs/img/instructions/img206235.jpg and /dev/null differ
diff --git a/docs/img/instructions/img206245.jpg b/docs/img/instructions/img206245.jpg
deleted file mode 100644
index dfd0d6a2..00000000
Binary files a/docs/img/instructions/img206245.jpg and /dev/null differ
diff --git a/docs/img/instructions/img206275.jpg b/docs/img/instructions/img206275.jpg
deleted file mode 100644
index b3a63218..00000000
Binary files a/docs/img/instructions/img206275.jpg and /dev/null differ
diff --git a/docs/img/instructions/img206310.jpg b/docs/img/instructions/img206310.jpg
deleted file mode 100644
index 8b2bf933..00000000
Binary files a/docs/img/instructions/img206310.jpg and /dev/null differ
diff --git a/docs/img/instructions/img206315.jpg b/docs/img/instructions/img206315.jpg
deleted file mode 100644
index cc764744..00000000
Binary files a/docs/img/instructions/img206315.jpg and /dev/null differ
diff --git a/docs/img/instructions/img206510.jpg b/docs/img/instructions/img206510.jpg
deleted file mode 100644
index 18233e7f..00000000
Binary files a/docs/img/instructions/img206510.jpg and /dev/null differ
diff --git a/docs/img/readme/hover_vid.jpg b/docs/img/readme/hover_vid.jpg
deleted file mode 100644
index 9a9b4a0b..00000000
Binary files a/docs/img/readme/hover_vid.jpg and /dev/null differ
diff --git a/docs/img/readme/rover_front_small.jpg b/docs/img/readme/rover_front_small.jpg
deleted file mode 100644
index 9046c7d0..00000000
Binary files a/docs/img/readme/rover_front_small.jpg and /dev/null differ
diff --git a/docs/img/startup/managed_ip.png b/docs/img/startup/managed_ip.png
deleted file mode 100644
index 68c90e6d..00000000
Binary files a/docs/img/startup/managed_ip.png and /dev/null differ
diff --git a/docs/img/zerotier/client_application_menu.png b/docs/img/zerotier/client_application_menu.png
deleted file mode 100644
index 562ae0e4..00000000
Binary files a/docs/img/zerotier/client_application_menu.png and /dev/null differ
diff --git a/docs/img/zerotier/enter_network_id.png b/docs/img/zerotier/enter_network_id.png
deleted file mode 100644
index e6bf0d25..00000000
Binary files a/docs/img/zerotier/enter_network_id.png and /dev/null differ
diff --git a/docs/img/zerotier/hub_menu.png b/docs/img/zerotier/hub_menu.png
deleted file mode 100644
index d070a586..00000000
Binary files a/docs/img/zerotier/hub_menu.png and /dev/null differ
diff --git a/docs/img/zerotier/hub_network_members.png b/docs/img/zerotier/hub_network_members.png
deleted file mode 100644
index 64587188..00000000
Binary files a/docs/img/zerotier/hub_network_members.png and /dev/null differ
diff --git a/docs/img/zerotier/hub_overview_small.png b/docs/img/zerotier/hub_overview_small.png
deleted file mode 100644
index d66b9482..00000000
Binary files a/docs/img/zerotier/hub_overview_small.png and /dev/null differ
diff --git a/docs/img/zerotier/login_email_password.png b/docs/img/zerotier/login_email_password.png
deleted file mode 100644
index cd32b25b..00000000
Binary files a/docs/img/zerotier/login_email_password.png and /dev/null differ
diff --git a/docs/img/zerotier/login_to_zerotier.png b/docs/img/zerotier/login_to_zerotier.png
deleted file mode 100644
index 9804a20f..00000000
Binary files a/docs/img/zerotier/login_to_zerotier.png and /dev/null differ
diff --git a/docs/img/zerotier/manually_add_member.png b/docs/img/zerotier/manually_add_member.png
deleted file mode 100644
index 5f0f81c0..00000000
Binary files a/docs/img/zerotier/manually_add_member.png and /dev/null differ
diff --git a/docs/img/zerotier/network_id_and_name.png b/docs/img/zerotier/network_id_and_name.png
deleted file mode 100644
index 52be9f0e..00000000
Binary files a/docs/img/zerotier/network_id_and_name.png and /dev/null differ
diff --git a/docs/img/zerotier/your_networks.png b/docs/img/zerotier/your_networks.png
deleted file mode 100644
index 6ba1f778..00000000
Binary files a/docs/img/zerotier/your_networks.png and /dev/null differ
diff --git a/docs/mwlc_assembly.md b/docs/mwlc_assembly.md
deleted file mode 100644
index 08abd18d..00000000
--- a/docs/mwlc_assembly.md
+++ /dev/null
@@ -1,115 +0,0 @@
-# Introduction
-#### Disclaimer
-This documentation provides information intended to help people build a self driving robot and to use it. The development of the robot is ongoing and it is not complete. The information in the documentation is subject to change. The documentation is provided free of charge.The robot as described in the documentation is a vehicle for testing software. We do not deploy the robot for applications that carry risk of any kind. Please pay attention to the guidelines and suggestions given, also for the use and charging of the battery. No explicit regulatory permission of any kind has been sought for deployment of the robot. This documentation has been put together with the greatest possible care. We are in no way responsible for any omissions and inaccuracies with regard to the information provided on the website, for whatever reason. We do not accept any liability.The information provided on the website is subject to certain intellectual property rights. Unless explicitly authorized to do so, you are not allowed to reproduce information provided on the website for commercial purposes of any kind.
-#### Instructions
-The instructions are exported from the development database and used by our technicians. The information might be a bit cryptic.
We are always open for improvements. Don't hesitate to [contact](http://www.mwlc.global/contact/) us, if you need some help or an extra picture.
-#### Content
-The robot is assembled in main stages:
- Chassis
- Body: External skeleton
- Body: Enclosure
- Cables, equipment
- Configuration
-# Chassis
-#### Arrma Kraton 8S
-The Kraton 8s is the largest Arrma model, but much too fast and with minimal carrying capacity.
To be able to carry more payload, the vehicle must be converted so that the motors for steering and driving last longer.
-Before the chassis can work for the robot we have to change some parts.
- Springs
- Steering servo
- Tires
-After unpacking take off:
- Cover Green/Orange ARA409001/7
- 2* Battery holders ARA320496
- Back spoiler, ARA480022
- Spoiler mount, ARA320492
- ARA320485
- 4* Wheels including tires ARA510122/520055/550061
- Some small parts like bolts and nuts
-#### Springs
-Change the springs.
To make the suspension more firm, first we change the springs to reduce pressure on the shocks.
Put the large springs on the place of the originals and the small springs around the shock shaft.
Find a video with instructions.
Note: The front springs are shorter than the back springs(ARA330572 and ARA330573)
-#### Esc tuning
-Change ESC settings according to Kraton 8S instruction manual.
- Low voltage cutoff(variable 3) to Low(setting 1)
- Brake strength(variable 5), 10%(setting 7)
- Reverse strength(variable 6), 75%(setting 3)
- Punch setting(variable 7), level 1(setting 1)Leave battery connected for next step
-#### Change steering servo
-This is a tricky operation, take some time to look for the video's, with instructions.
- Place batteries in hand unit
- Start the Kraton and the hand unit, set the steering wheels in straight forward
- Mark position servo saver both on the saver and the casing with something sharp, like a screwdriver, so you know afterwards if it fits correctly.
- Switch everything off
- Release ESC/Servo unit, 4 screws bottom
- Release cover, 6 small screws
- Release receiver, and wires from the box. Take the antenna wire out of the little tube.
- Release steering rod from servo saver to the steering connector on steering. Watch out not to loose little black lock nut m3, the bold will be replaced by a Allen screw, M3, 22 mm, quality 12.9.
-Release plastic contra bolt in servo saver(hex4). Watch out, releasing is clockwise!!!
- release bolt(hex2,5) far in servo saver to release is form the servo. Release safer with Allen screw driver.
- Take the new servo, connect the wire to the servo first.
- Take the new receiver box. Check if the new servo fits.
- Swap the data cable on the receiver.
Of the content of the package we don’t use the spring and the steering linkage.
Change the servo, take care it is positioned exactly right.
-The steering rod bold should be replaced with an Allen screw M3x22mm, steel 12.9.
-After changing the servo you can remove the receiver
-#### Wire shortcuts
-__ Red wires of the on/off button__
Shortcut the red wires of the on/off button
Use the orange ferrule double and the heat shrink.
Heat shrink 2,5 mm 2:1 covering the ferrule metal tube.
Heats chink 6 mm, 3:1, covering the ferrule.
-#### Cable gland bottom
-Before putting the enclosure on the robot, first put the cable gland around the cable from the ESC.
- Take a M25 Cable gland with cable gland nut, lock nut and outer seal ring.
- Put the gland nut over the cables for the ESC.(2 power cables and 2*2 thin data cables).
-- Take the multi seal or grommet, M25, 3 holes. Cut the holes open to put the cables through.
- First put small cable through one hole then the orange and black from the ESC, each through their own hole.
Then put cable gland around it. Screw the cable gland nut on the cable gland, not to tight.
- Ensure that the shortest wire, the steering servo connector is reachable in the enclosure. The gland must be quite tide to the chassis.
You can check this after making the bottom hole.
-# External skeleton
-The skeleton is built with the aluminum profile. 20 x 20 mm, groove 5.
In the end it should look like the picture.
-
-We have two kinds, both for groove 5 but 1 kind for Allen screw M5 and 1 kind for Allen screw M4 that is used for the brackets.
These options are also available in plate versions.
The blocks easy to work with but more expensive.
-
-#### Crossbeams
-Connect 2 crossbeams, 0,23m to the bottom of the chassis.
Use the empty holes from the battery holders and the M4 screws.
-#### Pillars(vertical profiles)
-Attach 4 pillars, 0,14m with brackets on the low crossbeams. The bracket are under the chassis.
-#### Longitudinal beams
-On the pillars the longitudinal beams are fastened.
At the right side 0,79m.
At the left side 0,85m.(longer to connect the back camera)
To protect the front camera, at the front, both beams will end in front of the front camera.
At the back, the right beam stops 2 cm after the end of the enclosure. The left beam goes further to the 6 cm back, to mount the back camera.
The longitudinal beams should be close to the bottom of the enclosure but don't push it up. The enclosure should rest on the towers of the chassis.
-#### Cross beams high
-On the longitudinal beams the high crossbeams of 0,23m are attached. They are close to the enclosure to keep it in place
-#### Lock pillars
-At both the front and the back side, attached to the high crossbeams, there should be the lock pillars of 0,13m.
It might use full to connect the front lock pillar with to brackets as the fornt-side of is often used replace the robot.
-The lock longitudinal beam, 0,90m, wil be attached to the lock pillars.
This beam is a bit longer to catch the hits.
Connect the beam with 2 brackets at each side for easy opening.
-#### Mount front Camera
-Bracket 150*150mm with 3 holes per side
Bracket 60*60mm with per side 2 holes
1* Allan screw M5 6mm
1* Allan screw M5 10mm
Tripod screw 8mm
2 repairwasher m5
2 washer m5
2 Slide nut
-
-Mount back Camera
-Bracket 150*150mm with 3 holes per side
Tripod screw
1 * Allan screw M5 10
6 washer M5
Slide nut
Extra bracket to lock camera
-# Enclosure
-In the end the enclosure will look more or less like in the picture.
But we have to admit, every build we find an improvement. Less cables, less space used, more easy to open.
In the instructions you find our latest build.
The switch is below the Nano.
Sometimes we get a request for a larger battery. This takes some space.
-
-__ Silica __
Don't forget to put in a pack of silica gel. But this can be done any time.
-#### Spelsberg AKL-4-t
-The Spelsberg enclosure AKL-4-t has transparant cover, metric pre-embossings.
Slightly cheaper alternative enclosure has a non transparant cover.
The enclosure should rest on the front and back towers of the chassis.
As the center tower is just slightly lower and the bottom of the enclosure has ridges. To make the bottom of the enclosure resting on the towers only, make a cross at the top of the centre tower to fit the enclosure in.
At the end we put some double sided adhesive tape at the towers.
-
-#### Holes, glands, connectors and buttons
-See scheme voor the location and size of the holes.
We use step drill bit to make or widening the holes.
It might be a suggestion to keep the part that needs the hole, nearby to see if it fits.
We always start with the bottom-hole and make is fit perfectly, even by putting the cables through the gland and see if the enclosure fits well.
-
-__ Bottom hole __
One of the more tricky parts is the location of the bottom hole for the M25 cable gland with the ESC power and datacables.
The outside bottom of the enclosure has ridge wich should stay intact.
Check with the already attached cable gland the location.
-__ Charge connector __
The screws to tighten the Rosenberger-connector need some space.
Avoid that the hole gets more to the top of the enclosre. While widening we press the drill to the bottom of the enclosure.
-__ Antenna cables __
The 5 antenna cables should be at the back-side left. Bring the cables around 0,15m within the enclosure.
- Put the cable gland nut over the cable connectors
- Take the black single seal ring from the gland and put it also over the connectors.
- Take the multi-seal with 5 holse, cut the holes en put the cables through.
Put around the multi-seal the big single seal from the cable gland.
First put the cable gland over the cables before putting is in het enclosure.
Don't make the cable gland to tide yet.
-__ WAN __
The WAN connector should be right-side of the enlcore most at the back.
The connector at the inside should point up with a small angle to the back.(The Lan connector will be above it)Use a self-adhesive cable mount, under the enclosure, to keep the cap to the vehicle wen not on the connector.
-__ LAN __
The Lan connector should be just above the WAN connector and a bit to the front.
-__ Cable gland back Camera __
Grommet qt 6, around camera cable between camera and first split.
Not too close to split. It should be possible to have the cable bend within the enclosure.
Take the two white parts ant put them around the grommet. The grommet fits just in one position exact right.
Take the sealing ring over the cable and put it around the gland.<>Pucht the gland though the enclosure.
Take the black splittable contra nut and tighten the gland to the enclosure.
- Put the black 0,25m ethernet cable in the connector.
- Put the 0,5m low voltage power cable in the connector.
-__ Cable gland front Camera __
Grommet around camera cable between camera and first split.
Not to close to split.
It should be possible to have the cable bend within the enclosure.
Cabel gland front camera works the same as back.
-__ Ignition switch __
Ignition switch is mounted at the same side as the LAN and WAN, just a bit to the fornt of the LAN.
-__ Stop button __
Stop button is mounted at the opposite side, left, the front, of the enclosure.
-# Cables and equipment
-#### Battery and internal frame
-__ Backside cable duct __
Put a piece, 0,28m, of cable duct against the back.
- Make it fit by taking the back side corners off.
- Take teeth off by the cable cland of the antenna's.
- Take teethparts off for the camera cable gland.
- Take 3 teethparts off for the Rosenbergerconnector.
Depending on the battery it might be handy to split the backside cable duct so it fits at both sides of the length cable duct.
-__Battery __
Put battery at back-left side in the enclosure, against the back side cable duct.
-__ Length cable duct __
Put a 0,58m cable duct in the middel over the full length of the enclosure against the right site of the battery.
- Make the back 3 cm flat so you can put it under the back cable duct or have the backside cable duct at both sides.
-__ Enclosure lay-out __
The battery is the largest component within the enclosure. As soon as the battery is in place, you can make a lay-out for the other components.
Collect the main components that should be in the enclosure and prepare the equiment holders for the Nano, Pi and the two USB-relays.
Think about how to fasten the ethernet-switch.
The best opption is not to need two layers and have all equipment on the bottom of the enclosure and on top of the battery.
The battery should be fixated.
-#### Equipment holders
-__ Holder for the Nano steered Relay __
Use Axxatronic 72mm:
- 2 end parts
- 1 narrow part
- 1 widepart.
-__ Holder for the Pi steered Relay __
See Relay Nano steered holder.
-__ Nano holder __
First, place SD card.
- Take PVC plate 107 x 100mm (or 72x80mm, if there is not a lot of space.
- Take 4 allen screws, 2,5x10mm
- Take 4 lock nuts 2,5mm
- Take 4 spacers
- Assemble Nano on the pvc plate, nuts topside, cable connections on the nano to the left.
- Put the plate with Nano on axxatronic 107mm( or 72mm) with foot.
-__ Pi holder __
First, place SD card.
Then mount the hat on the Pi with the mounting set that comes with the Pi- Take PVC plate 80 x 72mm
- Assemble the hat on the Pi on the pvc plate, wiIth the spacers and screws from the set delivered with the hat.
- Assemble the Pi on the pvc plate, wiIth allen screw 2,5x12.
- Put the plate with Pi on Axxatronic 72mm with foot.
-__Optional: internal frame __
You can use this if you can't find enough space on the bottom of the enclosure.
The internal frame consists of a few pieces of alluminium profile.
A small pillar close to the battery.
Connected to a horizontal piece to the front of the enclosure, that is conncted with a crossbeam to keep it stable.
Make a fitting frame.
3 pieces of profile:
- Pillar, from the bottom of the enclosure to the cover.
- Longitudinal, from the baterry to the front side.
- Crossbeam, at the front from left to right.
-__ Din rail __
1. Put 0,58m din rail over the length of the right side of the enclosure but not on the incoming ESC cables.
Put it against the backside cable duct.
2. Put din rail over the length of the left side of the enclosure. From the battery to the inner front side of the enclosure.
Put it against the backside cable duct.
-For a second layer you have to improvise with the din rail. For instance:
- ethernet switch to crossbeam profile of internal frame, 0,05m
- Computers, 0,25m
- 12vdc converter, 0,10m
- 2*5vdc converter, 0,10m
-# Cabling
-Start with putting in all component, losely, in the enclosure.
After al cables and wires are connected, fixate the component.
It might be usefull to have the schemes at hand.
While doing we also connect the ethernet cables, so keeps this scheme also nearby.
-__Hints__
Step by step the cabling voor the power will be assembled.
To avoid problems also some equipment and computer cables will be placed.
Some basics:
- Put cables and wires as much as possible in the cable ducts, without forcing of course.
- Use ferrules and cables with the right colours. This will help you later on.
- We connect the power per cicuit. I suppose there is more efficient methode but per circuit is simple.
- The cable length is more complex as we thought. If there are lengths on the scheme, just ignore them.
- I tailor the length on the moment I put it in. Take a wire in the right colour, put a ferrule on it. Connect 1 side. Lay it via the cable duct to the other end. Then cut it to the right length and put a ferrule on it.
- Not all wire have a ferrule at both sides. For instance in the fuse-holders and the RUT955 green terminal.
- Worst mistake is not tighten a wire enough to a connector. This is hard to test.
- The schemes with remarks are leading.
- Our working order is described.
-__ Back camera __
Fold the ethernet cable and power cable in the cable duct.
Plug in power wires in accoring to scheme, lay them in de cable duct direction RUT-distribution.
Connect the Ethernet with the router with a ethernet cable according to scheme.
Fold them in cable duct.
-__ WAN __
Put ethernet cable in.
-
-__ LAN __
Put ethernet cable in.
-__Ignition switch__
Put the adaptor plate horizontal on the inginition switch.
Put the NO-contact breaker on the lower position of the adaptor plate
-__ Stopbutton __
Put the adaptor plate on the stop button.
Put the NC-contact breaker on the adaptor plate
The positon can be choosen as suits you the best.
-__Battery and charge__Place 4 fuseholders on the din rail, between the ethernet passthrough's and the adaptar plate from the ignition switch.
Place 2 red terminal blocks on the din rail(option to disconnect on inside).
Place 2 black terminal blocks on the din rail(option to disconnect on inside).
Option: if the battery cable is more the 4mm2, take thick terminal block.
Option: if the battery has seperate cable for plus(red)-pole, take extra red terminal block.
Option: if the battery has seperate cable for minus(black)-pole, take extra black terminal block.
Put a shield against the open side of the last terminal block.
- Red wire from battery in the first fuse holder.
- Red wire from first fuseholder to first red terminal block.
- Red wire from charge connector into second fuse holder.
- Red wire from second fuseholder to second red terminal block.
- Option: If there is seperate extra red charge wire on the battery, connect this to the extra terminal block and lay the red wire from the second fuse not to the red terminal block but also to the extra red teminal block.
- Black wire from battery into (thick) black terminal block
- Black wire from charge connector into black terminal block
- Option: If there is seperate extra black charge wire on the battery put both the charging wire as the wire from the charger into the extra black terminal block.
-Create main distribution:
- Connect the black terminal blocks with bridge.
- Connect the red terminal blocks with bridge.
This thus not include the optional charge terminal blocks.
-__ Terminal blocks ESC input __
Put the terminals in back from the cable-gland and connect the power.
-
-__ SSR(Steady State Relay)__
Put the SSR losely on the din rail in the second part of the enclosure.
Connect the power according to scheme, via the third fuse.
After connecting the control wires in port 1 and 2, the SSR can be fixated.
-
-__ Power cabling to 12VDC __
Put the 12vdc losely in place.
The 12vdc power start with the ignition key. After the ignition al other system will be started.
Connect according to scheme with the battery power.
The incoming power should be place against the wall of the enclosure.
-
-__ Rut955 PoE __
Connect power from the first set of 12vdc-converter with the PoE-injector for the RUT955 via the fourth fuse.
-__ Place RUT-distrbution __
Put beside the ESC connector:
- 6 red thin terminal blocks
- Connect them with a 5 bridge and a 2 bridge
- 6 black thin terminal blocks
- Connect them with a 5 bridge and a 2 bridge
Put them losely.
Plug in the incoming wires from the power source in the back side.
Then you can fixate them.
Put wires to the end-power consumer on the fornt.
-
-__ Connect RUT-distribution __
See scheme.
This goes via the relay on the RUT955.
-
-__ Ethernet switch __
Connect the power of the ethernet switch.
-
-__ Front camera __
Plug in power wires in according to scheme
-__ Back camera __
Plug in power wires in according to scheme
-__ Power Nano__
Plug in power wires in according to scheme, via a 5vdc converter
-__ Power Pi __
Plug in power wires in according to scheme via the 5vdc-conveter and the stop-button.
-__Remaing SSR control wires__
Make the remaing connetions of the control for the SSR with the RUT-distribution.
-__ Grove cable Pi to steering and ESC __
Plug in Grove-cable in according to scheme.
-__ Ethernet Pi Nano front camera __
Plug in power wires in according to scheme
-# Testing
-When starting upo the following light and sound should apperar:
- Blue 12vdc
- Orange RUT
Wait
- 5vdc
- Camera check
- Lights ethernet
- Lights Nano
- Light Nano steered relay
- Lights Pi
-Close the encloser and use the locker profile to fixate the encloser on the chassis.
-# Questions and suggestion
-The assembly instructions are user driven and created based on questions to give guidance on certain aspects.
If you have any question, don't hesitate to contact us at [www.mwlc.global/contact/](http://www.mwlc.global/contact/).
diff --git a/following/.DS_Store b/following/.DS_Store
new file mode 100644
index 00000000..09f64491
Binary files /dev/null and b/following/.DS_Store differ
diff --git a/following/Dockerfile b/following/Dockerfile
new file mode 100644
index 00000000..9813ca75
--- /dev/null
+++ b/following/Dockerfile
@@ -0,0 +1,41 @@
+FROM ultralytics/ultralytics:8.2.48-jetson-jetpack4
+
+# Don't prompt with any configuration questions
+ENV DEBIAN_FRONTEND noninteractive
+
+# Install some utils
+RUN apt-get update && apt-get install -y lbzip2 git wget unzip jq xorg tar python3 libegl1 binutils xz-utils bzip2 build-essential python3-dev python3-pip
+
+ENV UDEV=1
+
+# Download and install BSP binaries for L4T 32.7.3, note new nonstandard URL
+RUN \
+ cd /tmp/ && wget https://developer.nvidia.com/downloads/remetpack-463r32releasev73t210jetson-210linur3273aarch64tbz2 && \
+ tar xf remetpack-463r32releasev73t210jetson-210linur3273aarch64tbz2 && rm remetpack-463r32releasev73t210jetson-210linur3273aarch64tbz2 && \
+ cd Linux_for_Tegra && \
+ sed -i 's/config.tbz2\"/config.tbz2\" --exclude=etc\/hosts --exclude=etc\/hostname/g' apply_binaries.sh && \
+ sed -i 's/install --owner=root --group=root \"${QEMU_BIN}\" \"${L4T_ROOTFS_DIR}\/usr\/bin\/\"/#install --owner=root --group=root \"${QEMU_BIN}\" \"${L4T_ROOTFS_DIR}\/usr\/bin\/\"/g' nv_tegra/nv-apply-debs.sh && \
+ sed -i 's/chroot . \// /g' nv_tegra/nv-apply-debs.sh && \
+ ./apply_binaries.sh -r / --target-overlay && cd .. \
+ rm -rf Linux_for_Tegra && \
+ echo "/usr/lib/aarch64-linux-gnu/tegra" > /etc/ld.so.conf.d/nvidia-tegra.conf && ldconfig
+
+
+RUN pip3 install --upgrade pyzmq lap simple-pid
+
+# Copy your application files
+COPY ./common/ /common/
+COPY ./following/ /app/
+WORKDIR /app
+
+ENV PYTHONPATH "/app:/common:${PYTHONPATH}"
+
+# Should use this one in the future. to keep the imports also desciverable by the local env. Will need to add `common.` at the beginning of each byodr import
+# COPY ./common/ /app/common/
+# COPY ./following/ /app/following/
+# WORKDIR /app/following
+# ENV PYTHONPATH "/app:${PYTHONPATH}"
+
+
+# Command to run your application
+CMD ["python3", "app.py"]
\ No newline at end of file
diff --git a/following/app.py b/following/app.py
new file mode 100644
index 00000000..baeb7e5f
--- /dev/null
+++ b/following/app.py
@@ -0,0 +1,106 @@
+import argparse
+import configparser
+import glob
+import logging
+import multiprocessing
+import os
+import signal
+
+from byodr.utils import Application, hash_dict
+from byodr.utils.ipc import json_collector
+from fol_utils import FollowingController
+
+logger = logging.getLogger(__name__)
+
+log_format = "%(levelname)s: %(asctime)s %(filename)s:%(lineno)d %(funcName)s %(threadName)s %(message)s"
+
+# Initialize quit event
+quit_event = multiprocessing.Event()
+
+
+def _interrupt():
+ logger.info("Received interrupt, quitting.")
+ quit_event.set()
+
+
+signal.signal(signal.SIGINT, lambda sig, frame: _interrupt())
+signal.signal(signal.SIGTERM, lambda sig, frame: _interrupt())
+
+
+class FollowingApplication(Application):
+ def __init__(self, event, hz, config_dir=os.getcwd()):
+ super(FollowingApplication, self).__init__(quit_event=event, run_hz=hz)
+ self._config_dir = config_dir
+ self._user_config_file = os.path.join(self._config_dir, "config.ini")
+ self._config_hash = -1
+ self.controller = FollowingController(model_path="./models/yolov8_20240717_coco(imgsz480x640_FP16).engine", user_config_args=self.get_user_config_file_contents(), event=quit_event, hz=hz)
+
+ def _check_user_config(self):
+ """See if there is an available user config file, and assign it to the self var"""
+ _candidates = glob.glob(os.path.join(self._config_dir, "*.ini"))
+ for file_path in _candidates:
+ # Extract the filename from the path
+ file_name = os.path.basename(file_path)
+ if file_name == "config.ini":
+ self._user_config_file = file_path
+
+ def get_user_config_file(self):
+ return self._user_config_file
+
+ def get_user_config_file_contents(self):
+ """Reads the configuration file, flattens the configuration sections and keys"""
+ config = configparser.ConfigParser()
+ config.read(self.get_user_config_file())
+
+ config_dict = {f"{section}.{option}": value for section in config.sections() for option, value in config.items(section)}
+ return config_dict
+
+ def setup(self):
+ if self.active():
+ self._check_user_config()
+ _config = self.get_user_config_file_contents()
+ _hash = hash_dict(**_config)
+ if _hash != self._config_hash:
+ self._config_hash = _hash
+
+ def step(self):
+ self.controller.request_check()
+
+
+def main():
+ parser = argparse.ArgumentParser(description="Following sockets server.")
+ parser.add_argument("--name", type=str, default="none", help="Process name.")
+ parser.add_argument("--config", type=str, default="/config", help="Config directory path.")
+ args = parser.parse_args()
+
+ # Communication sockets.
+ teleop_cha = json_collector(url="ipc:///byodr/teleop_c.sock", topic=b"aav/teleop/chatter", pop=True, event=quit_event, hwm=1)
+ application = FollowingApplication(event=quit_event, config_dir=args.config, hz=20)
+
+ # Getting data from the received sockets declared above
+ application.controller.teleop_chatter = lambda: teleop_cha.get()
+ application.controller.initialize_following()
+ application.setup()
+ threads = [teleop_cha]
+
+ if quit_event.is_set():
+ return 0
+
+ [t.start() for t in threads]
+
+ try:
+ application.run()
+ except KeyboardInterrupt:
+ quit_event.set()
+ except Exception as e:
+ logger.error(f"Exception occurred: {e}")
+ quit_event.set()
+
+ logger.info("Waiting on threads to stop.")
+ [t.join() for t in threads]
+
+
+if __name__ == "__main__":
+ logging.basicConfig(format=log_format, datefmt="%Y%m%d:%H:%M:%S %p %Z")
+ logging.getLogger().setLevel(logging.INFO)
+ main()
diff --git a/following/botsort.yaml b/following/botsort.yaml
new file mode 100644
index 00000000..6db8c9f0
--- /dev/null
+++ b/following/botsort.yaml
@@ -0,0 +1,31 @@
+# Ultralytics YOLO 🚀, AGPL-3.0 license
+# Default YOLO tracker settings for BoT-SORT tracker https://github.com/NirAharon/BoT-SORT
+
+tracker_type: botsort # tracker type, ['botsort', 'bytetrack']
+track_high_thresh: 0.4 # threshold for the first association
+track_low_thresh: 0.05 # threshold for the second association
+new_track_thresh: 0.3 # lower threshold for initializing new tracks
+track_buffer: 500 # increase buffer to maintain tracks longer
+match_thresh: 0.5
+# min_box_area: 10 # threshold for min box areas(for tracker evaluation, not used for now)
+# mot20: False # for tracker evaluation(not used for now)
+
+# BoT-SORT settings
+# for tracking in scenarios with camera movement or background motion
+gmc_method: sparseOptFlow # method of global motion compensation
+# ReID model related thresh (not supported yet)
+proximity_thresh: 0.6 # increase ReID proximity threshold
+appearance_thresh: 0.35 # increase ReID appearance threshold
+with_reid: True
+
+
+# ✅tracker_type: botsort: Tracker type, with options ['botsort', 'bytetrack']. Choose between BoT-SORT or ByteTrack for object tracking.
+# ✅track_high_thresh: 0.5: Threshold for the first association. Detections with a confidence score above this threshold will be considered for tracking.
+# ✅track_low_thresh: 0.1: Threshold for the second association. This threshold is used for refining track associations.
+# ✅new_track_thresh: 0.6: Threshold for initializing a new track if the detection doesn't match any existing tracks. This parameter influences the creation of new object tracks.
+# ✅track_buffer: 30: Buffer to calculate the time when to remove tracks. It helps manage the retention period for tracks in the tracking system. Measured in frames
+# ✅match_thresh: This parameter determines the level of similarity required for associating detections with existing tracks.
+# ✅gmc_method:I is used for tracking in scenarios with camera movement or background motion
+# ✅proximity_thresh: 0.5: Threshold for proximity. It plays a role in determining the closeness criteria for objects or tracks.
+# ✅appearance_thresh: 0.25: Threshold for appearance. This parameter relates to the confidence level in matching the appearance features of objects during tracking.
+# ✅with_reid: False: Indicates whether ReID (Re-identification) is enabled (currently set to False). ReID is a technique used for identifying and distinguishing objects across different frames.
\ No newline at end of file
diff --git a/following/fol_utils.py b/following/fol_utils.py
new file mode 100644
index 00000000..cc4cd8a9
--- /dev/null
+++ b/following/fol_utils.py
@@ -0,0 +1,378 @@
+import logging
+import multiprocessing
+import os
+import threading
+import time
+
+import cv2
+from byodr.utils.ipc import JSONPublisher
+from byodr.utils.option import parse_option
+from ultralytics import YOLO
+
+logger = logging.getLogger(__name__)
+quit_event = multiprocessing.Event()
+
+
+class YoloInference:
+ """Handles everything related to YOLO inference and obtaining detection results."""
+
+ def __init__(self, model_path, user_config_args):
+ self.user_config_args = user_config_args
+ self.model_path = model_path
+ self.model = YOLO(model_path, task="detect")
+ self.image_save_path = "/byodr/yolo_person"
+ self.image_counter = 0
+ self.results = None
+ os.makedirs(self.image_save_path, exist_ok=True)
+
+ def run(self):
+ stream_uri = self.get_stream_uri()
+ # The persist=True argument tells the tracker that the current image or frame is the next in a sequence and to expect tracks from the previous image in the current image.
+ # Streaming mode is beneficial for processing videos or live streams as it creates a generator of results instead of loading all frames into memory.
+ # image size is a tuple of (h, w)
+ self.results = self.model.track(source=stream_uri, classes=0, stream=True, conf=0.35, persist=True, verbose=False, imgsz=(480, 640), tracker="botsort.yaml")
+ logger.info(f"{self.model_path} model is loaded")
+
+ def get_stream_uri(self):
+ bottom_camera_uri = parse_option("camera.front.camera.ip", str, "192.168.1.64", [], **self.user_config_args)
+ return f"rtsp://user1:HaikuPlot876@{bottom_camera_uri}:554/Streaming/Channels/103"
+
+ def draw_boxes(self, img, results, followed_person_id=None, width=None, height=None):
+ """Draw boxes on detected objects (persons) in the returned tensor"""
+ for r in results:
+ boxes = r.boxes
+
+ for box in boxes:
+ # normalized bounding box
+ x1n, y1n, x2n, y2n = box.xyxyn[0]
+ # scale to image size
+ x1, y1, x2, y2 = int(x1n * width), int(y1n * height), int(x2n * width), int(y2n * height)
+
+ # text and background
+ label = None
+ person_id = int(box.id[0]) if box.id is not None and len(box.id) > 0 and box.id[0] is not None else -1 # Ensure person_id is an int
+
+ if person_id == followed_person_id:
+ label = "Followed"
+ else:
+ label = "Not Followed"
+
+ font = cv2.FONT_HERSHEY_DUPLEX
+ font_scale = 1
+ thickness = 2
+ text_size = cv2.getTextSize(label, font, font_scale, thickness)[0]
+ text_x, text_y = x1, y1
+ text_w, text_h = text_size[0], text_size[1]
+
+ # background rectangle for text
+ cv2.rectangle(img, (text_x, text_y - text_h), (text_x + text_w, text_y), (255, 0, 255), -1)
+
+ # text on the background
+ cv2.putText(img, label, (text_x, text_y - 2), font, font_scale, (255, 255, 255), thickness)
+
+ # put bounding box in the image
+ cv2.rectangle(img, (x1, y1), (x2, y2), (255, 0, 255), 2)
+
+ def track_and_save_image(self, result, followed_person_id):
+ """Tracks objects in video stream and saves the latest image with annotations."""
+ img = result.orig_img # get the original image
+ img_height, img_width = result.orig_shape
+ img = cv2.resize(img, (img_width // 2, img_height // 2)) # Resize the image to decrease the bandwidth on mobile controller
+ self.draw_boxes(img, [result], followed_person_id, width=(img_width // 2), height=(img_height // 2)) # pass the followed person ID and new dimensions
+ filename = os.path.join(self.image_save_path, f"image_{self.image_counter}.jpg")
+ cv2.imwrite(filename, img)
+ self.image_counter += 1
+
+ # Manage saved images to keep only the latest 10
+ all_images = sorted(os.listdir(self.image_save_path), key=lambda x: os.path.getctime(os.path.join(self.image_save_path, x)))
+ if len(all_images) > 10:
+ oldest_image = all_images[0]
+ os.remove(os.path.join(self.image_save_path, oldest_image))
+
+ def reset_tracking_session(self):
+ """Reset the image counter and clear all images in the directory."""
+ self.image_counter = 0
+ for existing_file in os.listdir(self.image_save_path):
+ os.remove(os.path.join(self.image_save_path, existing_file))
+
+
+class CommandController:
+ """Handles command calculations and publishes them."""
+
+ def __init__(self, user_config_args):
+ self.user_config_args = user_config_args
+ self.current_throttle = 0
+ self.current_steering = 0
+ self.current_camera_pan = 0
+ self.current_azimuth = 0
+ self.person_height = 0
+ self.followed_person_id = None
+ self.publisher = JSONPublisher(url="ipc:///byodr/following.sock", topic="aav/following/controls")
+ self.calibration_flag = False
+ self.get_fol_configs()
+
+ def get_fol_configs(self):
+ self.pan_movement_offset = parse_option("camera.pan_movement_offset", int, 50, [], **self.user_config_args)
+ self.left_red_zone = parse_option("following.left_red_zone", float, 0.45, [], **self.user_config_args) # In percentage
+ self.right_red_zone = parse_option("following.right_red_zone", float, 0.55, [], **self.user_config_args) # In percentage
+ self.max_camera_pan = parse_option("following.max_camera_pan", float, 3500, [], **self.user_config_args) # max is 3550
+ self.min_camera_pan = parse_option("following.min_camera_pan", float, 50, [], **self.user_config_args) # value increments from 0 when panning right
+ self.unsafe_height = parse_option("following.unsafe_height", float, 0.65, [], **self.user_config_args) # In percentage
+ self.steering_adjustment_factor = parse_option("following.steering_adjustment_factor", int, 3, [], **self.user_config_args)
+
+ self.original_left_red_zone = self.left_red_zone
+ self.original_right_red_zone = self.right_red_zone
+
+ def update_control_commands(self, persons):
+ """Updates camera pan, steering, and throttle based on the operator's position on the screen."""
+ lowest_id = float("inf")
+ person_to_follow = {}
+
+ try:
+ # Iterate through each person detected
+ for person in persons:
+ try:
+ self.person_height = person.xywhn[0][3]
+ # Check if person is too close to the camera
+ if self.person_height >= self.unsafe_height:
+ self.current_throttle = 0
+ self.current_steering = 0
+ self.current_camera_pan = self.calculate_camera_pan(person.xywhn[0][0])
+ return # Exit the loop early
+ # Skip the person if there is no ID assigned
+ if person.id is None or len(person.id) == 0 or person.id[0] is None:
+ continue
+
+ person_id = int(person.id[0])
+ # print(person)
+ if person_id < lowest_id:
+ lowest_id = person_id
+ person_to_follow = {"id": person_id, "x_center_percentage": person.xywhn[0][0]}
+ except TypeError as e:
+ logger.error(f"Error processing person: {person} with error {e}")
+
+ # If no person was too close, proceed with updating the commands
+ if person_to_follow:
+ x_center_percentage = person_to_follow["x_center_percentage"]
+ self.followed_person_id = int(person_to_follow["id"])
+ self.current_throttle = self.calculate_throttle()
+
+ self.check_calibration_needed(x_center_percentage)
+ if self.calibration_flag:
+ self.perform_calibration() # It will override camera pan and vehicle steering
+ else:
+ self.current_camera_pan = self.calculate_camera_pan(x_center_percentage)
+ self.current_steering = self.calculate_steering(x_center_percentage)
+
+ except Exception as e:
+ logger.error(f"Exception updating control commands: {e}, with person_to_follow: {person_to_follow}")
+
+ def check_calibration_needed(self, x_center_percentage):
+ """Check if calibration is needed and raise the flag if necessary."""
+ # Calibration can only start if the user is in the middle of camera view and the azimuth isn't less than min or more than max value
+ # (if it is between them, that means it isn't pointing forward)
+ if (self.left_red_zone <= x_center_percentage <= self.right_red_zone) and (self.min_camera_pan <= self.current_azimuth <= self.max_camera_pan):
+ self.calibration_flag = True
+ # Nearly half of original value
+ self.left_red_zone = 0.275
+ self.right_red_zone = 0.85
+ else:
+ self.calibration_flag = False
+ self.left_red_zone = self.original_left_red_zone
+ self.right_red_zone = self.original_right_red_zone
+
+ def perform_calibration(self):
+ """Perform the calibration process to align the vehicle with the camera direction."""
+ if self.current_azimuth < 1800:
+ self.current_steering = (self.current_azimuth - 1800) / 1800 / self.steering_adjustment_factor
+ else:
+ self.current_steering = (1800 - self.current_azimuth) / 1800 / self.steering_adjustment_factor
+
+ self.current_steering = max(-1, min(1, self.current_steering))
+ self.current_camera_pan = -self.current_steering * self.pan_movement_offset
+ logger.info(f"S:{self.current_steering:.2f} C_P:{self.current_camera_pan:.2f}")
+
+ def calculate_throttle(self):
+ return max(0.2, min(1, ((-(0.01) * self.person_height) + 3.6))) # 0.2 at 340p height; 1 at 260p height
+
+ def calculate_camera_pan(self, x_center_percentage):
+ """Calculate camera pan to follow the user depending on their position in the camera view."""
+ try:
+ if x_center_percentage < self.left_red_zone:
+ pan_adjustment = -self.pan_movement_offset * ((self.left_red_zone - x_center_percentage) / self.left_red_zone)
+ elif x_center_percentage > self.right_red_zone:
+ pan_adjustment = self.pan_movement_offset * ((x_center_percentage - self.right_red_zone) / (1 - self.right_red_zone))
+ else:
+ pan_adjustment = 0
+ return pan_adjustment
+ except Exception as e:
+ logger.error(f"Failed to calculate camera pan: {e}")
+ return 0 # Return 0 to maintain neutral camera position on error
+
+ def calculate_steering(self, x_center_percentage):
+ try:
+ if x_center_percentage < self.left_red_zone:
+ return -1 + (x_center_percentage / self.left_red_zone)
+ elif x_center_percentage > self.right_red_zone:
+ return (x_center_percentage - self.right_red_zone) / (1 - self.right_red_zone)
+ else:
+ return 0
+ except Exception as e:
+ logger.error(f"Error calculating steering: {e}")
+ return 0 # Return neutral steering on error
+
+ def publish_command(self, throttle=0, steering=0, camera_pan=0, source="Following"):
+ """Publishes the control commands to Teleop."""
+ try:
+ throttle = round(throttle, 3)
+ steering = round(steering, 3)
+
+ cmd = {"throttle": throttle, "steering": steering, "source": source}
+
+ if camera_pan is not None and source == "followingActive":
+ cmd["button_b"] = 1
+ # Make sure to define the preset for the camera
+ if camera_pan == "go_preset_1":
+ cmd["camera_pan"] = camera_pan
+ elif camera_pan != 0:
+ cmd["camera_pan"] = int(camera_pan)
+ self.publisher.publish(cmd)
+ if source == "followingActive" and (cmd["throttle"] != 0 or cmd["steering"] != 0):
+ print(f'Control commands: T:{cmd["throttle"]}, S:{cmd["steering"]}, C_P:{cmd.get("camera_pan", "N/A")}')
+ # logger.info(cmd)
+ except Exception as e:
+ logger.warning(f"Error while sending teleop command {e}")
+
+ def reset_control_commands(self):
+ self.current_throttle = 0
+ self.current_steering = 0
+ self.current_camera_pan = 0
+ self.publish_command(self.current_throttle, self.current_steering, self.current_camera_pan)
+
+
+class FollowingController:
+ """Coordinates between `YoloInference` and `CommandController`."""
+
+ def __init__(self, model_path, user_config_args, event, hz):
+ self.yolo_inference = YoloInference(model_path, user_config_args)
+ self.command_controller = CommandController(user_config_args)
+ self.stop_yolo = threading.Event()
+ self.quit_event = event
+ self.hz = hz
+ self.run_yolo_inf = None
+ self.stop_threads = False
+ self.fol_state = "loading"
+
+ def initialize_following(self):
+ """Initial delay and command to indicate loading state."""
+ # As the connection is PUB(FOL)-SUB(TEL), there's no built-in mechanism to ensure that subscriber is ready to receive messages before the publisher starts sending them
+ time.sleep(2)
+ self.command_controller.publish_command(source="followingLoading")
+ self.yolo_inference.run()
+ self.fol_state = "inactive"
+
+ def request_check(self):
+ """Fetches and processes the request from Teleop."""
+ try:
+ teleop_request = self.teleop_chatter()
+ if teleop_request is not None:
+ if teleop_request.get("following") == "start_following":
+ self._start_following()
+ elif teleop_request.get("following") == "stop_following":
+ self._stop_following()
+ elif teleop_request.get("camera_azimuth") is not None:
+ self.command_controller.current_azimuth = int(teleop_request.get("camera_azimuth"))
+ # print(self.command_controller.current_azimuth)
+ self._publish_current_state()
+ except Exception as e:
+ logger.error(f"Exception in request_check: {e}")
+ quit_event.set()
+
+ def _start_following(self):
+ """Start the following process."""
+ if self.run_yolo_inf is None or not self.run_yolo_inf.is_alive():
+ self.fol_state = "active"
+ self.command_controller.publish_command(
+ self.command_controller.current_throttle,
+ self.command_controller.current_steering,
+ camera_pan="go_preset_1",
+ source="followingActive",
+ )
+ self.yolo_inference.reset_tracking_session()
+ self.stop_threads = False
+ self.run_yolo_inf = threading.Thread(target=self._control_logic, args=(lambda: self.stop_threads,))
+ self.run_yolo_inf.start()
+
+ def _stop_following(self):
+ """Stop the following process."""
+ logger.info("Stopping Following")
+ self.command_controller.reset_control_commands()
+ if self.run_yolo_inf is not None and self.run_yolo_inf.is_alive():
+ self.fol_state = "loading"
+ self.command_controller.publish_command(source="followingLoading")
+ self.stop_threads = True
+ self.run_yolo_inf.join()
+
+ def _publish_current_state(self):
+ """Publish the current state of following."""
+ # In case there isn't a command received from teleop_chatter and it isn't active or loading, it should send followingInactive
+ if self.fol_state == "inactive":
+ self.command_controller.publish_command(source="followingInactive")
+ # As the model sends when it has new data, there sin't a way for the ui to keep showing the stream box. This condition here is to cover for these gaps
+ elif self.fol_state == "active":
+ self.command_controller.publish_command(
+ throttle=self.command_controller.current_throttle,
+ steering=self.command_controller.current_steering,
+ camera_pan=self.command_controller.current_camera_pan,
+ source="followingActive",
+ )
+
+ def _control_logic(self, stop):
+ """Processes each frame of YOLO output with a controlled rate."""
+ frame_time = 1 / self.hz # Calculate the time each frame should take based on the Hz rate
+ try:
+ for r in self.yolo_inference.results:
+ start_time = time.time() # Record the start time of the loop iteration
+ if stop():
+ logger.info("YOLO model stopped")
+ self.fol_state = "inactive"
+ self.command_controller.publish_command(source="followingReady")
+ break
+ self._update_control_commands(r.boxes.cpu().numpy())
+ followed_person_id = self.command_controller.followed_person_id
+ self.yolo_inference.track_and_save_image(r, followed_person_id)
+ self.end_to_end_inf_time = round(r.speed["preprocess"] + r.speed["inference"] + r.speed["postprocess"], 2) # end-to-end result is in ms
+ # Calculate the actual time to sleep to maintain the frame rate
+ sleep_time = frame_time - (time.time() - start_time)
+ if sleep_time > 0:
+ time.sleep(sleep_time) # Sleep to maintain the desired frame rate
+ except Exception as e:
+ logger.error(f"Exception in _control_logic: {e}")
+ self.quit_event.set()
+
+ def get_current_model_fps(self):
+ """Calculate and return the current frames per second. It is based on the end-to-end speed of the model, not how fast the whole app is working"""
+ if self.end_to_end_inf_time == 0:
+ return 0 # Return zero FPS to indicate no processing is happening or to avoid division by zero.
+ return round(1000 / self.end_to_end_inf_time, 2) # Total time is in ms, so 1000 ms / total time gives FPS.
+
+ def _update_control_commands(self, boxes):
+ """Update control commands based on detected boxes."""
+ if len(boxes) == 0:
+ self.command_controller.current_throttle = 0
+ self.command_controller.current_steering = 0
+ self.command_controller.current_camera_pan = 0
+ self.command_controller.publish_command(
+ throttle=self.command_controller.current_throttle,
+ steering=self.command_controller.current_steering,
+ camera_pan=self.command_controller.current_camera_pan,
+ source="followingActive",
+ )
+ else:
+ self.command_controller.update_control_commands(boxes)
+ self.command_controller.publish_command(
+ throttle=self.command_controller.current_throttle,
+ steering=self.command_controller.current_steering,
+ camera_pan=self.command_controller.current_camera_pan,
+ source="followingActive",
+ )
diff --git a/following/models/480_20k.pt b/following/models/480_20k.pt
new file mode 100644
index 00000000..cb753612
Binary files /dev/null and b/following/models/480_20k.pt differ
diff --git a/following/models/args.yaml b/following/models/args.yaml
new file mode 100644
index 00000000..4646c4a8
--- /dev/null
+++ b/following/models/args.yaml
@@ -0,0 +1,109 @@
+task: detect
+mode: train
+model: train/yolov8n_blank(imgsz480x640_FP16_485epoch).pt
+data: /home/ahmedmahfouz/.local/lib/python3.10/site-packages/ultralytics/cfg/datasets/coco.yaml
+epochs: 600
+time: null
+patience: 60
+batch: 16
+imgsz:
+- 480
+- 640
+save: true
+save_period: -1
+cache: false
+device: 0
+workers: 16
+project: training_runs
+name: run_240703_T_1619
+exist_ok: false
+pretrained: true
+optimizer: SGD
+verbose: false
+seed: 0
+deterministic: true
+single_cls: false
+rect: false
+cos_lr: false
+close_mosaic: 10
+resume: train/yolov8n_blank(imgsz480x640_FP16_485epoch).pt
+amp: true
+fraction: 1.0
+profile: false
+freeze: null
+multi_scale: false
+overlap_mask: true
+mask_ratio: 4
+dropout: 0.0
+val: true
+split: val
+save_json: false
+save_hybrid: false
+conf: null
+iou: 0.7
+max_det: 300
+half: false
+dnn: false
+plots: true
+source: null
+vid_stride: 1
+stream_buffer: false
+visualize: false
+augment: true
+agnostic_nms: false
+classes:
+- 0
+retina_masks: false
+embed: null
+show: false
+save_frames: false
+save_txt: false
+save_conf: false
+save_crop: false
+show_labels: true
+show_conf: true
+show_boxes: true
+line_width: null
+format: torchscript
+keras: false
+optimize: false
+int8: false
+dynamic: false
+simplify: false
+opset: null
+workspace: 8
+nms: false
+lr0: 0.01
+lrf: 0.01
+momentum: 0.937
+weight_decay: 0.0005
+warmup_epochs: 3.0
+warmup_momentum: 0.8
+warmup_bias_lr: 0.1
+box: 7.5
+cls: 0.5
+dfl: 1.5
+pose: 12.0
+kobj: 1.0
+label_smoothing: 0.0
+nbs: 64
+hsv_h: 0.05
+hsv_s: 0.75
+hsv_v: 0.45
+degrees: 5
+translate: 0.2
+scale: 0.6
+shear: 20
+perspective: 0.001
+flipud: 0.0
+fliplr: 0.5
+bgr: 0.0
+mosaic: 1.0
+mixup: 0.1
+copy_paste: 0.0
+auto_augment: randaugment
+erasing: 0.3
+crop_fraction: 0.9
+cfg: null
+tracker: botsort.yaml
+save_dir: training_runs/run_240703_T_1619
diff --git a/following/models/yolov8_20240717_coco(imgsz480x640_FP16).engine b/following/models/yolov8_20240717_coco(imgsz480x640_FP16).engine
new file mode 100644
index 00000000..64c59bd8
Binary files /dev/null and b/following/models/yolov8_20240717_coco(imgsz480x640_FP16).engine differ
diff --git a/following/models/yolov8n.engine b/following/models/yolov8n.engine
new file mode 100644
index 00000000..6517e0d1
Binary files /dev/null and b/following/models/yolov8n.engine differ
diff --git a/following/models/yolov8n.pt b/following/models/yolov8n.pt
new file mode 100644
index 00000000..d61ef50d
Binary files /dev/null and b/following/models/yolov8n.pt differ
diff --git a/following/models/yolov8n_coco(imgsz480x640_FP16_485epoch).csv b/following/models/yolov8n_coco(imgsz480x640_FP16_485epoch).csv
new file mode 100644
index 00000000..05a30c79
--- /dev/null
+++ b/following/models/yolov8n_coco(imgsz480x640_FP16_485epoch).csv
@@ -0,0 +1,577 @@
+epoch ,train/box_loss ,train/cls_loss ,train/dfl_loss ,metrics/precision(B) ,metrics/recall(B) ,metrics/mAP50(B) ,metrics/mAP50-95(B) ,val/box_loss ,val/cls_loss ,val/dfl_loss ,lr/pg0 ,lr/pg1 ,lr/pg2
+ 1 , 2.7456 , 3.2508 , 2.7771 , 0.40996 , 0.25953 , 0.25303 , 0.10764 , 2.1603 , 2.2433 , 2.1611 ,0.070004 ,0.0033329 ,0.0033329
+ 2 , 2.0417 , 2.353 , 2.0873 , 0.57619 , 0.39111 , 0.43524 , 0.22175 , 1.7725 , 1.7631 , 1.7954 ,0.039993 ,0.0066552 ,0.0066552
+ 3 , 1.8994 , 2.1014 , 1.9368 , 0.63646 , 0.44038 , 0.50764 , 0.2772 , 1.631 , 1.5556 , 1.6558 ,0.0099711 ,0.0099666 ,0.0099666
+ 4 , 1.8175 , 1.9519 , 1.8459 , 0.66773 , 0.47963 , 0.55092 , 0.31044 , 1.5703 , 1.4418 , 1.5865 ,0.0099505 ,0.0099505 ,0.0099505
+ 5 , 1.7519 , 1.8278 , 1.782 , 0.69328 , 0.50005 , 0.57477 , 0.33272 , 1.5198 , 1.364 , 1.5458 ,0.009934 ,0.009934 ,0.009934
+ 6 , 1.7163 , 1.7586 , 1.7421 , 0.70479 , 0.50877 , 0.58822 , 0.34619 , 1.4821 , 1.3159 , 1.5142 ,0.0099175 ,0.0099175 ,0.0099175
+ 7 , 1.6871 , 1.7078 , 1.7108 , 0.70841 , 0.52029 , 0.5981 , 0.35559 , 1.4618 , 1.2915 , 1.4963 ,0.009901 ,0.009901 ,0.009901
+ 8 , 1.6681 , 1.6704 , 1.6868 , 0.71507 , 0.52529 , 0.60393 , 0.36041 , 1.4473 , 1.2742 , 1.483 ,0.0098845 ,0.0098845 ,0.0098845
+ 9 , 1.6551 , 1.6462 , 1.669 , 0.71784 , 0.52686 , 0.60768 , 0.36464 , 1.4365 , 1.2624 , 1.473 ,0.009868 ,0.009868 ,0.009868
+ 10 , 1.6327 , 1.6223 , 1.6477 , 0.71817 , 0.53002 , 0.61132 , 0.37007 , 1.4223 , 1.2518 , 1.4618 ,0.0098515 ,0.0098515 ,0.0098515
+ 11 , 1.6144 , 1.602 , 1.6249 , 0.71947 , 0.52973 , 0.61366 , 0.37427 , 1.4094 , 1.2417 , 1.45 ,0.009835 ,0.009835 ,0.009835
+ 12 , 1.5988 , 1.5814 , 1.6119 , 0.72512 , 0.5324 , 0.6172 , 0.3783 , 1.3966 , 1.2316 , 1.4384 ,0.0098185 ,0.0098185 ,0.0098185
+ 13 , 1.5808 , 1.5661 , 1.5923 , 0.72616 , 0.53596 , 0.62047 , 0.38265 , 1.3822 , 1.2226 , 1.4258 ,0.009802 ,0.009802 ,0.009802
+ 14 , 1.5717 , 1.5515 , 1.5805 , 0.72779 , 0.53763 , 0.6238 , 0.38734 , 1.3685 , 1.2119 , 1.4144 ,0.0097855 ,0.0097855 ,0.0097855
+ 15 , 1.5612 , 1.5399 , 1.5689 , 0.73228 , 0.53911 , 0.62718 , 0.39193 , 1.3553 , 1.1989 , 1.403 ,0.009769 ,0.009769 ,0.009769
+ 16 , 1.5517 , 1.5299 , 1.5635 , 0.74032 , 0.53976 , 0.63101 , 0.39546 , 1.342 , 1.1881 , 1.3916 ,0.0097525 ,0.0097525 ,0.0097525
+ 17 , 1.5489 , 1.523 , 1.554 , 0.73995 , 0.54561 , 0.6349 , 0.3997 , 1.3298 , 1.1773 , 1.3804 ,0.009736 ,0.009736 ,0.009736
+ 18 , 1.5399 , 1.5149 , 1.5453 , 0.74318 , 0.54691 , 0.63825 , 0.4032 , 1.3157 , 1.1667 , 1.3688 ,0.0097195 ,0.0097195 ,0.0097195
+ 19 , 1.5326 , 1.5082 , 1.5355 , 0.74486 , 0.54876 , 0.64205 , 0.4075 , 1.3039 , 1.1567 , 1.3583 ,0.009703 ,0.009703 ,0.009703
+ 20 , 1.5234 , 1.4996 , 1.5271 , 0.74994 , 0.55145 , 0.64601 , 0.41111 , 1.2939 , 1.1453 , 1.3492 ,0.0096865 ,0.0096865 ,0.0096865
+ 21 , 1.5125 , 1.4926 , 1.5198 , 0.75314 , 0.55498 , 0.64976 , 0.4147 , 1.2831 , 1.1343 , 1.3392 ,0.00967 ,0.00967 ,0.00967
+ 22 , 1.5084 , 1.4884 , 1.5137 , 0.75328 , 0.55795 , 0.65303 , 0.41812 , 1.2739 , 1.1248 , 1.3308 ,0.0096535 ,0.0096535 ,0.0096535
+ 23 , 1.5052 , 1.4851 , 1.5101 , 0.75427 , 0.56017 , 0.65561 , 0.42107 , 1.2656 , 1.1153 , 1.3227 ,0.009637 ,0.009637 ,0.009637
+ 24 , 1.4963 , 1.4764 , 1.5022 , 0.75553 , 0.56408 , 0.65854 , 0.42376 , 1.2575 , 1.1046 , 1.3159 ,0.0096205 ,0.0096205 ,0.0096205
+ 25 , 1.4942 , 1.473 , 1.4957 , 0.75663 , 0.56519 , 0.6614 , 0.42626 , 1.2496 , 1.0943 , 1.3083 ,0.009604 ,0.009604 ,0.009604
+ 26 , 1.4863 , 1.4624 , 1.4914 , 0.75996 , 0.5667 , 0.66399 , 0.42877 , 1.2428 , 1.0851 , 1.3017 ,0.0095875 ,0.0095875 ,0.0095875
+ 27 , 1.4845 , 1.4647 , 1.4861 , 0.76133 , 0.56853 , 0.66582 , 0.43109 , 1.2379 , 1.0784 , 1.2959 ,0.009571 ,0.009571 ,0.009571
+ 28 , 1.4792 , 1.4564 , 1.4894 , 0.76106 , 0.56924 , 0.66665 , 0.43308 , 1.2329 , 1.0716 , 1.2914 ,0.0095545 ,0.0095545 ,0.0095545
+ 29 , 1.4774 , 1.4567 , 1.4793 , 0.7628 , 0.57112 , 0.66987 , 0.43504 , 1.2281 , 1.066 , 1.2863 ,0.009538 ,0.009538 ,0.009538
+ 30 , 1.4767 , 1.4552 , 1.477 , 0.76511 , 0.57085 , 0.67099 , 0.4366 , 1.2237 , 1.0598 , 1.2817 ,0.0095215 ,0.0095215 ,0.0095215
+ 31 , 1.4727 , 1.453 , 1.473 , 0.76534 , 0.57233 , 0.67218 , 0.43834 , 1.22 , 1.0545 , 1.2771 ,0.009505 ,0.009505 ,0.009505
+ 32 , 1.4718 , 1.4475 , 1.4685 , 0.76381 , 0.57374 , 0.67369 , 0.43969 , 1.2151 , 1.0488 , 1.2719 ,0.0094885 ,0.0094885 ,0.0094885
+ 33 , 1.4683 , 1.4471 , 1.4645 , 0.76611 , 0.57465 , 0.67608 , 0.44104 , 1.2109 , 1.0432 , 1.267 ,0.009472 ,0.009472 ,0.009472
+ 34 , 1.4661 , 1.4422 , 1.4627 , 0.76516 , 0.57641 , 0.67761 , 0.44253 , 1.208 , 1.0393 , 1.2631 ,0.0094555 ,0.0094555 ,0.0094555
+ 35 , 1.4652 , 1.4396 , 1.4596 , 0.76206 , 0.57979 , 0.6794 , 0.44393 , 1.2055 , 1.034 , 1.2593 ,0.009439 ,0.009439 ,0.009439
+ 36 , 1.4623 , 1.4405 , 1.4558 , 0.76002 , 0.58303 , 0.68133 , 0.44556 , 1.2034 , 1.0271 , 1.2565 ,0.0094225 ,0.0094225 ,0.0094225
+ 37 , 1.459 , 1.4369 , 1.4537 , 0.76887 , 0.57966 , 0.68222 , 0.44653 , 1.2009 , 1.0239 , 1.2532 ,0.009406 ,0.009406 ,0.009406
+ 38 , 1.456 , 1.4329 , 1.4482 , 0.76484 , 0.58625 , 0.68351 , 0.44807 , 1.198 , 1.0197 , 1.2496 ,0.0093895 ,0.0093895 ,0.0093895
+ 39 , 1.4602 , 1.4316 , 1.448 , 0.7651 , 0.58504 , 0.68551 , 0.44895 , 1.1959 , 1.0156 , 1.2467 ,0.009373 ,0.009373 ,0.009373
+ 40 , 1.4561 , 1.4279 , 1.4458 , 0.76333 , 0.58718 , 0.68654 , 0.45053 , 1.1936 , 1.0117 , 1.2441 ,0.0093565 ,0.0093565 ,0.0093565
+ 41 , 1.4503 , 1.4242 , 1.4412 , 0.76153 , 0.58931 , 0.68683 , 0.4513 , 1.1915 , 1.0072 , 1.2417 ,0.00934 ,0.00934 ,0.00934
+ 42 , 1.4523 , 1.4259 , 1.4409 , 0.76558 , 0.59061 , 0.68759 , 0.452 , 1.1892 , 1.0036 , 1.239 ,0.0093235 ,0.0093235 ,0.0093235
+ 43 , 1.4469 , 1.4197 , 1.4365 , 0.7626 , 0.59256 , 0.68907 , 0.45322 , 1.1875 , 0.99998 , 1.2366 ,0.009307 ,0.009307 ,0.009307
+ 44 , 1.4452 , 1.4164 , 1.4333 , 0.76639 , 0.59219 , 0.68922 , 0.45403 , 1.1854 , 0.99835 , 1.2344 ,0.0092905 ,0.0092905 ,0.0092905
+ 45 , 1.4491 , 1.4178 , 1.4358 , 0.76594 , 0.59182 , 0.68954 , 0.45479 , 1.1839 , 0.99532 , 1.2328 ,0.009274 ,0.009274 ,0.009274
+ 46 , 1.4469 , 1.4147 , 1.4307 , 0.76949 , 0.59182 , 0.69037 , 0.45557 , 1.1825 , 0.99224 , 1.2308 ,0.0092575 ,0.0092575 ,0.0092575
+ 47 , 1.4459 , 1.4143 , 1.4297 , 0.76695 , 0.59488 , 0.69136 , 0.45667 , 1.1806 , 0.98898 , 1.2288 ,0.009241 ,0.009241 ,0.009241
+ 48 , 1.4441 , 1.411 , 1.4287 , 0.76816 , 0.59553 , 0.69191 , 0.4576 , 1.1797 , 0.98672 , 1.2271 ,0.0092245 ,0.0092245 ,0.0092245
+ 49 , 1.4407 , 1.4098 , 1.4263 , 0.76579 , 0.59831 , 0.69253 , 0.45771 , 1.1787 , 0.98433 , 1.2258 ,0.009208 ,0.009208 ,0.009208
+ 50 , 1.4399 , 1.4118 , 1.4265 , 0.76673 , 0.59868 , 0.69333 , 0.45881 , 1.1782 , 0.98295 , 1.2248 ,0.0091915 ,0.0091915 ,0.0091915
+ 51 , 1.4393 , 1.4064 , 1.4243 , 0.76671 , 0.60063 , 0.69378 , 0.45895 , 1.177 , 0.98057 , 1.2235 ,0.009175 ,0.009175 ,0.009175
+ 52 , 1.4346 , 1.4062 , 1.4186 , 0.77382 , 0.59872 , 0.6948 , 0.45996 , 1.1744 , 0.9788 , 1.2213 ,0.0091585 ,0.0091585 ,0.0091585
+ 53 , 1.4368 , 1.4041 , 1.422 , 0.77675 , 0.59757 , 0.69559 , 0.4606 , 1.173 , 0.97684 , 1.2192 ,0.009142 ,0.009142 ,0.009142
+ 54 , 1.4354 , 1.3988 , 1.4192 , 0.77713 , 0.59571 , 0.6958 , 0.4609 , 1.1723 , 0.97609 , 1.2181 ,0.0091255 ,0.0091255 ,0.0091255
+ 55 , 1.4356 , 1.3987 , 1.4172 , 0.77963 , 0.5968 , 0.69583 , 0.46131 , 1.1718 , 0.97469 , 1.2168 ,0.009109 ,0.009109 ,0.009109
+ 56 , 1.4329 , 1.3977 , 1.416 , 0.77932 , 0.59933 , 0.69647 , 0.46203 , 1.1706 , 0.97289 , 1.2151 ,0.0090925 ,0.0090925 ,0.0090925
+ 57 , 1.4306 , 1.3957 , 1.4119 , 0.78164 , 0.59831 , 0.69654 , 0.46241 , 1.1691 , 0.97131 , 1.2133 ,0.009076 ,0.009076 ,0.009076
+ 58 , 1.4333 , 1.3967 , 1.4135 , 0.7814 , 0.59869 , 0.69684 , 0.46243 , 1.1682 , 0.96992 , 1.2124 ,0.0090595 ,0.0090595 ,0.0090595
+ 59 , 1.4198 , 1.3797 , 1.407 , 0.78328 , 0.59828 , 0.69777 , 0.46354 , 1.1662 , 0.96658 , 1.2107 ,0.009043 ,0.009043 ,0.009043
+ 60 , 1.4298 , 1.3958 , 1.4134 , 0.78222 , 0.59905 , 0.69811 , 0.46386 , 1.1652 , 0.96355 , 1.2102 ,0.0090265 ,0.0090265 ,0.0090265
+ 61 , 1.4328 , 1.3983 , 1.4182 , 0.78204 , 0.59927 , 0.69861 , 0.46445 , 1.1638 , 0.96176 , 1.2092 ,0.00901 ,0.00901 ,0.00901
+ 62 , 1.4334 , 1.3978 , 1.4161 , 0.78026 , 0.59905 , 0.69904 , 0.46455 , 1.1636 , 0.96054 , 1.2089 ,0.0089935 ,0.0089935 ,0.0089935
+ 63 , 1.4315 , 1.3998 , 1.4115 , 0.77877 , 0.59942 , 0.69869 , 0.46506 , 1.1631 , 0.96005 , 1.2075 ,0.008977 ,0.008977 ,0.008977
+ 64 , 1.4321 , 1.3968 , 1.4129 , 0.77782 , 0.59933 , 0.69915 , 0.46489 , 1.1617 , 0.95904 , 1.2063 ,0.0089605 ,0.0089605 ,0.0089605
+ 65 , 1.429 , 1.3946 , 1.4105 , 0.77777 , 0.60046 , 0.69925 , 0.46498 , 1.16 , 0.95783 , 1.2052 ,0.008944 ,0.008944 ,0.008944
+ 66 , 1.4279 , 1.3929 , 1.4072 , 0.7764 , 0.60251 , 0.69942 , 0.46616 , 1.1578 , 0.95685 , 1.2036 ,0.0089275 ,0.0089275 ,0.0089275
+ 67 , 1.429 , 1.3923 , 1.4062 , 0.77477 , 0.60444 , 0.69911 , 0.4663 , 1.1578 , 0.95721 , 1.203 ,0.008911 ,0.008911 ,0.008911
+ 68 , 1.4262 , 1.3912 , 1.4057 , 0.77383 , 0.60369 , 0.69944 , 0.46686 , 1.1572 , 0.95506 , 1.2016 ,0.0088945 ,0.0088945 ,0.0088945
+ 69 , 1.4253 , 1.3919 , 1.4038 , 0.77617 , 0.60406 , 0.6994 , 0.46735 , 1.1576 , 0.95482 , 1.2006 ,0.008878 ,0.008878 ,0.008878
+ 70 , 1.4269 , 1.3899 , 1.4058 , 0.77699 , 0.60261 , 0.69955 , 0.46805 , 1.1569 , 0.95449 , 1.1995 ,0.0088615 ,0.0088615 ,0.0088615
+ 71 , 1.4189 , 1.3872 , 1.3992 , 0.7822 , 0.59951 , 0.69957 , 0.46804 , 1.1557 , 0.95396 , 1.1985 ,0.008845 ,0.008845 ,0.008845
+ 72 , 1.4224 , 1.3841 , 1.3988 , 0.7811 , 0.59989 , 0.69971 , 0.46852 , 1.1557 , 0.95433 , 1.1978 ,0.0088285 ,0.0088285 ,0.0088285
+ 73 , 1.4208 , 1.3841 , 1.3995 , 0.78048 , 0.59978 , 0.69923 , 0.4686 , 1.1553 , 0.95419 , 1.1972 ,0.008812 ,0.008812 ,0.008812
+ 74 , 1.4172 , 1.3818 , 1.3981 , 0.78128 , 0.59915 , 0.69967 , 0.46882 , 1.1544 , 0.95248 , 1.1957 ,0.0087955 ,0.0087955 ,0.0087955
+ 75 , 1.4204 , 1.3815 , 1.3965 , 0.77472 , 0.60119 , 0.69973 , 0.46898 , 1.1533 , 0.95173 , 1.1943 ,0.008779 ,0.008779 ,0.008779
+ 76 , 1.4217 , 1.3824 , 1.3987 , 0.77369 , 0.59961 , 0.70011 , 0.4692 , 1.1534 , 0.95116 , 1.1938 ,0.0087625 ,0.0087625 ,0.0087625
+ 77 , 1.4203 , 1.3836 , 1.3935 , 0.77477 , 0.60212 , 0.70091 , 0.46909 , 1.1531 , 0.95002 , 1.1933 ,0.008746 ,0.008746 ,0.008746
+ 78 , 1.4188 , 1.3784 , 1.3937 , 0.77284 , 0.60379 , 0.70188 , 0.4692 , 1.1536 , 0.94849 , 1.1937 ,0.0087295 ,0.0087295 ,0.0087295
+ 79 , 1.4153 , 1.3806 , 1.3939 , 0.77557 , 0.6036 , 0.70186 , 0.46948 , 1.1527 , 0.94813 , 1.1926 ,0.008713 ,0.008713 ,0.008713
+ 80 , 1.4163 , 1.3766 , 1.3918 , 0.77878 , 0.60267 , 0.70246 , 0.46992 , 1.1532 , 0.9484 , 1.1927 ,0.0086965 ,0.0086965 ,0.0086965
+ 81 , 1.4184 , 1.3823 , 1.3935 , 0.77967 , 0.60249 , 0.70248 , 0.46969 , 1.1542 , 0.94766 , 1.193 ,0.00868 ,0.00868 ,0.00868
+ 82 , 1.4131 , 1.3744 , 1.388 , 0.78148 , 0.60054 , 0.70287 , 0.47028 , 1.1527 , 0.94546 , 1.1916 ,0.0086635 ,0.0086635 ,0.0086635
+ 83 , 1.4161 , 1.3769 , 1.3887 , 0.78205 , 0.60332 , 0.7037 , 0.47037 , 1.1534 , 0.94546 , 1.1915 ,0.008647 ,0.008647 ,0.008647
+ 84 , 1.4107 , 1.3689 , 1.3871 , 0.78188 , 0.6037 , 0.7049 , 0.47079 , 1.1525 , 0.94469 , 1.1902 ,0.0086305 ,0.0086305 ,0.0086305
+ 85 , 1.4122 , 1.3736 , 1.386 , 0.78435 , 0.60137 , 0.70522 , 0.47081 , 1.1515 , 0.9439 , 1.1891 ,0.008614 ,0.008614 ,0.008614
+ 86 , 1.4089 , 1.368 , 1.3885 , 0.78635 , 0.60017 , 0.70571 , 0.47154 , 1.1513 , 0.94352 , 1.1892 ,0.0085975 ,0.0085975 ,0.0085975
+ 87 , 1.4112 , 1.3675 , 1.3858 , 0.78966 , 0.60093 , 0.70555 , 0.47211 , 1.1522 , 0.94137 , 1.1892 ,0.008581 ,0.008581 ,0.008581
+ 88 , 1.4117 , 1.3731 , 1.3854 , 0.78832 , 0.60239 , 0.70569 , 0.47207 , 1.1512 , 0.94152 , 1.188 ,0.0085645 ,0.0085645 ,0.0085645
+ 89 , 1.4106 , 1.372 , 1.3848 , 0.78726 , 0.60402 , 0.70534 , 0.47188 , 1.1502 , 0.94211 , 1.1874 ,0.008548 ,0.008548 ,0.008548
+ 90 , 1.4104 , 1.3688 , 1.382 , 0.78709 , 0.6036 , 0.70549 , 0.47231 , 1.1495 , 0.94237 , 1.1864 ,0.0085315 ,0.0085315 ,0.0085315
+ 91 , 1.4117 , 1.3689 , 1.3804 , 0.78793 , 0.60297 , 0.70531 , 0.47242 , 1.1496 , 0.94195 , 1.1865 ,0.008515 ,0.008515 ,0.008515
+ 92 , 1.4081 , 1.3666 , 1.38 , 0.7851 , 0.60306 , 0.70588 , 0.47303 , 1.1486 , 0.94104 , 1.1854 ,0.0084985 ,0.0084985 ,0.0084985
+ 93 , 1.4071 , 1.3664 , 1.3803 , 0.78491 , 0.60441 , 0.70587 , 0.4736 , 1.1479 , 0.94018 , 1.1846 ,0.008482 ,0.008482 ,0.008482
+ 94 , 1.4097 , 1.3691 , 1.3811 , 0.78002 , 0.60676 , 0.70627 , 0.47414 , 1.1457 , 0.93831 , 1.1828 ,0.0084655 ,0.0084655 ,0.0084655
+ 95 , 1.4054 , 1.3664 , 1.3783 , 0.78228 , 0.60666 , 0.7061 , 0.47427 , 1.1445 , 0.93687 , 1.1819 ,0.008449 ,0.008449 ,0.008449
+ 96 , 1.4047 , 1.3644 , 1.3761 , 0.7819 , 0.60703 , 0.70619 , 0.47454 , 1.143 , 0.93571 , 1.181 ,0.0084325 ,0.0084325 ,0.0084325
+ 97 , 1.4087 , 1.3642 , 1.3773 , 0.78258 , 0.60587 , 0.70627 , 0.47493 , 1.1426 , 0.93551 , 1.1801 ,0.008416 ,0.008416 ,0.008416
+ 98 , 1.4062 , 1.363 , 1.3775 , 0.78545 , 0.6062 , 0.7068 , 0.47474 , 1.1437 , 0.93533 , 1.1803 ,0.0083995 ,0.0083995 ,0.0083995
+ 99 , 1.4017 , 1.3584 , 1.3725 , 0.78717 , 0.60583 , 0.70712 , 0.47481 , 1.1425 , 0.9341 , 1.1794 ,0.008383 ,0.008383 ,0.008383
+ 100 , 1.4052 , 1.3636 , 1.3764 , 0.78274 , 0.60861 , 0.7077 , 0.4753 , 1.1421 , 0.93433 , 1.1795 ,0.0083665 ,0.0083665 ,0.0083665
+ 101 , 1.4017 , 1.3584 , 1.373 , 0.78442 , 0.60722 , 0.70769 , 0.47514 , 1.1423 , 0.93341 , 1.1788 ,0.00835 ,0.00835 ,0.00835
+ 102 , 1.4013 , 1.3561 , 1.3729 , 0.78107 , 0.60972 , 0.70737 , 0.47546 , 1.1424 , 0.9325 , 1.1787 ,0.0083335 ,0.0083335 ,0.0083335
+ 103 , 1.4036 , 1.3613 , 1.3739 , 0.77906 , 0.61084 , 0.70784 , 0.47569 , 1.143 , 0.93164 , 1.1786 ,0.008317 ,0.008317 ,0.008317
+ 104 , 1.4046 , 1.358 , 1.3705 , 0.781 , 0.60713 , 0.70758 , 0.47553 , 1.1428 , 0.93188 , 1.1778 ,0.0083005 ,0.0083005 ,0.0083005
+ 105 , 1.4028 , 1.3602 , 1.3723 , 0.77855 , 0.60972 , 0.70769 , 0.47584 , 1.1417 , 0.93005 , 1.1768 ,0.008284 ,0.008284 ,0.008284
+ 106 , 1.4005 , 1.3553 , 1.3699 , 0.78174 , 0.6088 , 0.70785 , 0.47598 , 1.1426 , 0.92911 , 1.1768 ,0.0082675 ,0.0082675 ,0.0082675
+ 107 , 1.3998 , 1.3524 , 1.3681 , 0.7838 , 0.60815 , 0.70841 , 0.47594 , 1.1422 , 0.93032 , 1.1755 ,0.008251 ,0.008251 ,0.008251
+ 108 , 1.3995 , 1.3567 , 1.3664 , 0.78383 , 0.60991 , 0.70819 , 0.47616 , 1.1435 , 0.92965 , 1.1754 ,0.0082345 ,0.0082345 ,0.0082345
+ 109 , 1.3998 , 1.3528 , 1.3695 , 0.77936 , 0.61093 , 0.70837 , 0.47664 , 1.1424 , 0.92973 , 1.1748 ,0.008218 ,0.008218 ,0.008218
+ 110 , 1.3948 , 1.3512 , 1.3645 , 0.77465 , 0.61492 , 0.70883 , 0.47654 , 1.1429 , 0.92924 , 1.1743 ,0.0082015 ,0.0082015 ,0.0082015
+ 111 , 1.3968 , 1.3501 , 1.3656 , 0.7783 , 0.61177 , 0.70858 , 0.47682 , 1.143 , 0.92933 , 1.1735 ,0.008185 ,0.008185 ,0.008185
+ 112 , 1.3964 , 1.3462 , 1.3639 , 0.77809 , 0.61167 , 0.70967 , 0.4768 , 1.1428 , 0.92871 , 1.1726 ,0.0081685 ,0.0081685 ,0.0081685
+ 113 , 1.3961 , 1.3442 , 1.3617 , 0.77615 , 0.61354 , 0.7099 , 0.47649 , 1.1437 , 0.92875 , 1.1723 ,0.008152 ,0.008152 ,0.008152
+ 114 , 1.3963 , 1.3448 , 1.3632 , 0.77519 , 0.61501 , 0.71009 , 0.4763 , 1.1441 , 0.9284 , 1.1724 ,0.0081355 ,0.0081355 ,0.0081355
+ 115 , 1.3941 , 1.3453 , 1.3622 , 0.77976 , 0.61202 , 0.71011 , 0.47656 , 1.1443 , 0.92804 , 1.1726 ,0.008119 ,0.008119 ,0.008119
+ 116 , 1.3957 , 1.3474 , 1.3633 , 0.78215 , 0.61362 , 0.71018 , 0.47658 , 1.1431 , 0.92698 , 1.1718 ,0.0081025 ,0.0081025 ,0.0081025
+ 117 , 1.3957 , 1.348 , 1.3595 , 0.78324 , 0.61167 , 0.71034 , 0.47708 , 1.1424 , 0.92579 , 1.171 ,0.008086 ,0.008086 ,0.008086
+ 118 , 1.3955 , 1.3453 , 1.3608 , 0.78193 , 0.61386 , 0.70991 , 0.47736 , 1.1419 , 0.92509 , 1.1705 ,0.0080695 ,0.0080695 ,0.0080695
+ 119 , 1.393 , 1.3465 , 1.3581 , 0.78001 , 0.6149 , 0.71005 , 0.47745 , 1.142 , 0.92525 , 1.1706 ,0.008053 ,0.008053 ,0.008053
+ 120 , 1.3937 , 1.3407 , 1.3604 , 0.77561 , 0.61687 , 0.71042 , 0.47692 , 1.141 , 0.92392 , 1.1695 ,0.0080365 ,0.0080365 ,0.0080365
+ 121 , 1.3953 , 1.3447 , 1.3587 , 0.77691 , 0.61826 , 0.71142 , 0.47808 , 1.14 , 0.92203 , 1.1687 ,0.00802 ,0.00802 ,0.00802
+ 122 , 1.3884 , 1.3428 , 1.3583 , 0.78351 , 0.61556 , 0.71187 , 0.47798 , 1.1391 , 0.92161 , 1.1684 ,0.0080035 ,0.0080035 ,0.0080035
+ 123 , 1.3928 , 1.345 , 1.3601 , 0.78154 , 0.61548 , 0.71212 , 0.47795 , 1.1386 , 0.92207 , 1.1682 ,0.007987 ,0.007987 ,0.007987
+ 124 , 1.3922 , 1.3448 , 1.3585 , 0.77691 , 0.61818 , 0.71234 , 0.47849 , 1.1371 , 0.92087 , 1.1669 ,0.0079705 ,0.0079705 ,0.0079705
+ 125 , 1.3893 , 1.3382 , 1.3558 , 0.78318 , 0.61368 , 0.71223 , 0.47881 , 1.1368 , 0.91994 , 1.166 ,0.007954 ,0.007954 ,0.007954
+ 126 , 1.3892 , 1.3396 , 1.3556 , 0.78006 , 0.61613 , 0.71213 , 0.47864 , 1.1359 , 0.92037 , 1.1655 ,0.0079375 ,0.0079375 ,0.0079375
+ 127 , 1.3899 , 1.3416 , 1.3564 , 0.78356 , 0.61406 , 0.71185 , 0.47907 , 1.1353 , 0.92092 , 1.1648 ,0.007921 ,0.007921 ,0.007921
+ 128 , 1.3845 , 1.3372 , 1.3538 , 0.78134 , 0.61715 , 0.71232 , 0.47899 , 1.1356 , 0.92017 , 1.1643 ,0.0079045 ,0.0079045 ,0.0079045
+ 129 , 1.3878 , 1.3359 , 1.353 , 0.7781 , 0.61821 , 0.71261 , 0.47891 , 1.1357 , 0.92018 , 1.1645 ,0.007888 ,0.007888 ,0.007888
+ 130 , 1.3914 , 1.34 , 1.3534 , 0.77996 , 0.61671 , 0.71247 , 0.47891 , 1.1354 , 0.91962 , 1.1636 ,0.0078715 ,0.0078715 ,0.0078715
+ 131 , 1.392 , 1.3433 , 1.3556 , 0.77851 , 0.61585 , 0.7121 , 0.47899 , 1.1361 , 0.91963 , 1.1642 ,0.007855 ,0.007855 ,0.007855
+ 132 , 1.3857 , 1.3341 , 1.3513 , 0.77361 , 0.61882 , 0.71229 , 0.47901 , 1.1353 , 0.91917 , 1.1635 ,0.0078385 ,0.0078385 ,0.0078385
+ 133 , 1.3889 , 1.3379 , 1.3509 , 0.77674 , 0.61596 , 0.71258 , 0.47976 , 1.1356 , 0.91828 , 1.163 ,0.007822 ,0.007822 ,0.007822
+ 134 , 1.3855 , 1.3339 , 1.3509 , 0.78341 , 0.61344 , 0.71217 , 0.47988 , 1.1366 , 0.91827 , 1.1632 ,0.0078055 ,0.0078055 ,0.0078055
+ 135 , 1.384 , 1.3325 , 1.3509 , 0.78319 , 0.61501 , 0.71265 , 0.47997 , 1.1363 , 0.91808 , 1.1624 ,0.007789 ,0.007789 ,0.007789
+ 136 , 1.3877 , 1.3388 , 1.352 , 0.7844 , 0.61743 , 0.71334 , 0.48056 , 1.1343 , 0.91783 , 1.1607 ,0.0077725 ,0.0077725 ,0.0077725
+ 137 , 1.3901 , 1.335 , 1.3494 , 0.78361 , 0.61678 , 0.71362 , 0.48087 , 1.1343 , 0.91723 , 1.1604 ,0.007756 ,0.007756 ,0.007756
+ 138 , 1.3847 , 1.3346 , 1.3481 , 0.78492 , 0.61474 , 0.71414 , 0.48137 , 1.1345 , 0.91614 , 1.1604 ,0.0077395 ,0.0077395 ,0.0077395
+ 139 , 1.3829 , 1.3336 , 1.3513 , 0.78819 , 0.61371 , 0.71405 , 0.48133 , 1.135 , 0.91478 , 1.1608 ,0.007723 ,0.007723 ,0.007723
+ 140 , 1.3839 , 1.3318 , 1.3472 , 0.78624 , 0.61639 , 0.71497 , 0.48192 , 1.1334 , 0.91372 , 1.1594 ,0.0077065 ,0.0077065 ,0.0077065
+ 141 , 1.3869 , 1.3352 , 1.3498 , 0.78838 , 0.61496 , 0.71453 , 0.48207 , 1.1328 , 0.91388 , 1.1593 ,0.00769 ,0.00769 ,0.00769
+ 142 , 1.3836 , 1.329 , 1.3472 , 0.78683 , 0.61546 , 0.71495 , 0.48232 , 1.1327 , 0.91368 , 1.159 ,0.0076735 ,0.0076735 ,0.0076735
+ 143 , 1.3863 , 1.3313 , 1.3461 , 0.78621 , 0.61232 , 0.71373 , 0.48193 , 1.1328 , 0.91387 , 1.1589 ,0.007657 ,0.007657 ,0.007657
+ 143 , 1.3673 , 1.3087 , 1.3375 , 0.78702 , 0.61641 , 0.7145 , 0.4821 , 1.1319 , 0.91258 , 1.1585 ,0.007657 ,0.007657 ,0.007657
+ 144 , 1.3783 , 1.3243 , 1.343 , 0.78799 , 0.61458 , 0.71519 , 0.4824 , 1.1321 , 0.91129 , 1.1586 ,0.0076405 ,0.0076405 ,0.0076405
+ 145 , 1.3822 , 1.3305 , 1.3475 , 0.79144 , 0.61408 , 0.7153 , 0.48256 , 1.1323 , 0.91048 , 1.1582 ,0.007624 ,0.007624 ,0.007624
+ 146 , 1.3825 , 1.3301 , 1.3464 , 0.78781 , 0.61634 , 0.71564 , 0.48206 , 1.1331 , 0.91009 , 1.1585 ,0.0076075 ,0.0076075 ,0.0076075
+ 147 , 1.3825 , 1.3313 , 1.3462 , 0.7878 , 0.61882 , 0.7158 , 0.48241 , 1.1325 , 0.91041 , 1.1581 ,0.007591 ,0.007591 ,0.007591
+ 148 , 1.3853 , 1.3304 , 1.3482 , 0.78686 , 0.61831 , 0.71539 , 0.48266 , 1.1322 , 0.91064 , 1.1577 ,0.0075745 ,0.0075745 ,0.0075745
+ 149 , 1.3829 , 1.3309 , 1.3453 , 0.78211 , 0.62151 , 0.71571 , 0.48262 , 1.1322 , 0.91132 , 1.1577 ,0.007558 ,0.007558 ,0.007558
+ 150 , 1.3809 , 1.3279 , 1.3434 , 0.77996 , 0.62198 , 0.71572 , 0.48263 , 1.1317 , 0.91017 , 1.1574 ,0.0075415 ,0.0075415 ,0.0075415
+ 151 , 1.3832 , 1.3305 , 1.3447 , 0.78195 , 0.62225 , 0.71624 , 0.48342 , 1.1307 , 0.90972 , 1.1566 ,0.007525 ,0.007525 ,0.007525
+ 152 , 1.3795 , 1.3269 , 1.3446 , 0.78508 , 0.61961 , 0.7168 , 0.48405 , 1.1297 , 0.90801 , 1.1557 ,0.0075085 ,0.0075085 ,0.0075085
+ 153 , 1.3804 , 1.3289 , 1.3445 , 0.78481 , 0.61794 , 0.71668 , 0.48421 , 1.1297 , 0.90668 , 1.1557 ,0.007492 ,0.007492 ,0.007492
+ 154 , 1.3816 , 1.3281 , 1.345 , 0.78307 , 0.61817 , 0.71672 , 0.48387 , 1.1307 , 0.90729 , 1.1562 ,0.0074755 ,0.0074755 ,0.0074755
+ 155 , 1.3752 , 1.326 , 1.3431 , 0.78553 , 0.61557 , 0.71615 , 0.48369 , 1.1304 , 0.90707 , 1.1562 ,0.007459 ,0.007459 ,0.007459
+ 156 , 1.3799 , 1.3246 , 1.3435 , 0.78751 , 0.61353 , 0.71589 , 0.48388 , 1.1296 , 0.90631 , 1.1563 ,0.0074425 ,0.0074425 ,0.0074425
+ 157 , 1.3782 , 1.3227 , 1.3446 , 0.78465 , 0.61715 , 0.71577 , 0.48414 , 1.1285 , 0.90517 , 1.1557 ,0.007426 ,0.007426 ,0.007426
+ 158 , 1.378 , 1.3226 , 1.3451 , 0.78604 , 0.61743 , 0.71614 , 0.48433 , 1.1283 , 0.9036 , 1.1553 ,0.0074095 ,0.0074095 ,0.0074095
+ 159 , 1.3807 , 1.3257 , 1.3435 , 0.79201 , 0.6165 , 0.71697 , 0.48481 , 1.1287 , 0.90388 , 1.1554 ,0.007393 ,0.007393 ,0.007393
+ 160 , 1.3791 , 1.3251 , 1.3438 , 0.78711 , 0.61753 , 0.71734 , 0.48484 , 1.1279 , 0.90351 , 1.1545 ,0.0073765 ,0.0073765 ,0.0073765
+ 161 , 1.3779 , 1.327 , 1.3403 , 0.79233 , 0.61529 , 0.71717 , 0.48469 , 1.1276 , 0.90412 , 1.1545 ,0.00736 ,0.00736 ,0.00736
+ 162 , 1.3798 , 1.3209 , 1.3393 , 0.79539 , 0.61344 , 0.71662 , 0.48452 , 1.1276 , 0.90461 , 1.1543 ,0.0073435 ,0.0073435 ,0.0073435
+ 163 , 1.3753 , 1.3226 , 1.3415 , 0.79009 , 0.61782 , 0.71697 , 0.4847 , 1.1276 , 0.90503 , 1.1543 ,0.007327 ,0.007327 ,0.007327
+ 164 , 1.3773 , 1.3206 , 1.3388 , 0.79314 , 0.61547 , 0.71709 , 0.48487 , 1.1272 , 0.90494 , 1.1536 ,0.0073105 ,0.0073105 ,0.0073105
+ 165 , 1.3792 , 1.324 , 1.3417 , 0.79706 , 0.61242 , 0.71699 , 0.48509 , 1.1271 , 0.90573 , 1.1534 ,0.007294 ,0.007294 ,0.007294
+ 166 , 1.3735 , 1.3189 , 1.3365 , 0.79438 , 0.61288 , 0.71695 , 0.48532 , 1.1271 , 0.90584 , 1.1527 ,0.0072775 ,0.0072775 ,0.0072775
+ 167 , 1.3771 , 1.3193 , 1.3376 , 0.79405 , 0.61499 , 0.71718 , 0.485 , 1.1261 , 0.90537 , 1.1516 ,0.007261 ,0.007261 ,0.007261
+ 168 , 1.3714 , 1.3138 , 1.3352 , 0.78835 , 0.61947 , 0.7174 , 0.48519 , 1.1249 , 0.90327 , 1.1507 ,0.0072445 ,0.0072445 ,0.0072445
+ 169 , 1.3748 , 1.3179 , 1.3351 , 0.78715 , 0.61845 , 0.7165 , 0.48549 , 1.1239 , 0.90283 , 1.15 ,0.007228 ,0.007228 ,0.007228
+ 170 , 1.3704 , 1.3124 , 1.3372 , 0.78476 , 0.61912 , 0.71661 , 0.48576 , 1.1229 , 0.90274 , 1.1493 ,0.0072115 ,0.0072115 ,0.0072115
+ 171 , 1.3712 , 1.3131 , 1.3357 , 0.78843 , 0.61725 , 0.71721 , 0.48611 , 1.1229 , 0.902 , 1.1491 ,0.007195 ,0.007195 ,0.007195
+ 172 , 1.3734 , 1.3192 , 1.3367 , 0.78781 , 0.61873 , 0.71742 , 0.48627 , 1.1225 , 0.90145 , 1.1488 ,0.0071785 ,0.0071785 ,0.0071785
+ 173 , 1.3718 , 1.3202 , 1.3357 , 0.78956 , 0.61826 , 0.71743 , 0.48652 , 1.122 , 0.90093 , 1.1487 ,0.007162 ,0.007162 ,0.007162
+ 174 , 1.3738 , 1.315 , 1.3379 , 0.79566 , 0.61733 , 0.71868 , 0.48707 , 1.1216 , 0.90124 , 1.1486 ,0.0071455 ,0.0071455 ,0.0071455
+ 175 , 1.376 , 1.3175 , 1.3358 , 0.79133 , 0.61891 , 0.71813 , 0.4872 , 1.1214 , 0.90159 , 1.1487 ,0.007129 ,0.007129 ,0.007129
+ 176 , 1.3717 , 1.3132 , 1.3331 , 0.79404 , 0.61511 , 0.71829 , 0.48737 , 1.1212 , 0.8999 , 1.1486 ,0.0071125 ,0.0071125 ,0.0071125
+ 177 , 1.3728 , 1.3137 , 1.3351 , 0.79858 , 0.61557 , 0.7186 , 0.48744 , 1.1208 , 0.89837 , 1.1482 ,0.007096 ,0.007096 ,0.007096
+ 178 , 1.3728 , 1.3178 , 1.3349 , 0.79631 , 0.61687 , 0.71937 , 0.48777 , 1.1199 , 0.89656 , 1.1471 ,0.0070795 ,0.0070795 ,0.0070795
+ 179 , 1.37 , 1.3164 , 1.3346 , 0.79822 , 0.61668 , 0.72057 , 0.48797 , 1.1193 , 0.89628 , 1.1471 ,0.007063 ,0.007063 ,0.007063
+ 180 , 1.3692 , 1.3113 , 1.3311 , 0.79986 , 0.61594 , 0.72029 , 0.48846 , 1.1191 , 0.89553 , 1.1472 ,0.0070465 ,0.0070465 ,0.0070465
+ 181 , 1.3719 , 1.3135 , 1.3337 , 0.79595 , 0.61835 , 0.72018 , 0.48845 , 1.1192 , 0.89608 , 1.1475 ,0.00703 ,0.00703 ,0.00703
+ 182 , 1.3689 , 1.3136 , 1.3325 , 0.79828 , 0.61837 , 0.72067 , 0.48839 , 1.1195 , 0.89595 , 1.1472 ,0.0070135 ,0.0070135 ,0.0070135
+ 183 , 1.3665 , 1.3095 , 1.3299 , 0.79814 , 0.61835 , 0.72077 , 0.48863 , 1.1198 , 0.89561 , 1.1474 ,0.006997 ,0.006997 ,0.006997
+ 184 , 1.3691 , 1.314 , 1.3313 , 0.8023 , 0.61483 , 0.72075 , 0.48867 , 1.1201 , 0.89543 , 1.148 ,0.0069805 ,0.0069805 ,0.0069805
+ 185 , 1.3659 , 1.3087 , 1.3314 , 0.80371 , 0.61319 , 0.72022 , 0.48888 , 1.1188 , 0.89485 , 1.1473 ,0.006964 ,0.006964 ,0.006964
+ 186 , 1.3662 , 1.3049 , 1.3288 , 0.80261 , 0.61384 , 0.72076 , 0.4886 , 1.1185 , 0.89403 , 1.1476 ,0.0069475 ,0.0069475 ,0.0069475
+ 187 , 1.3695 , 1.3107 , 1.3335 , 0.80113 , 0.61413 , 0.7209 , 0.48817 , 1.1177 , 0.89349 , 1.1468 ,0.006931 ,0.006931 ,0.006931
+ 188 , 1.3706 , 1.3079 , 1.3306 , 0.79897 , 0.61501 , 0.72071 , 0.4886 , 1.1169 , 0.89311 , 1.1461 ,0.0069145 ,0.0069145 ,0.0069145
+ 189 , 1.3692 , 1.3092 , 1.3305 , 0.79515 , 0.6178 , 0.71998 , 0.4886 , 1.1165 , 0.89353 , 1.1452 ,0.006898 ,0.006898 ,0.006898
+ 190 , 1.3666 , 1.3079 , 1.3303 , 0.79436 , 0.61956 , 0.72108 , 0.48892 , 1.1155 , 0.89298 , 1.1445 ,0.0068815 ,0.0068815 ,0.0068815
+ 191 , 1.3661 , 1.3047 , 1.3286 , 0.79394 , 0.61863 , 0.72083 , 0.48887 , 1.1161 , 0.89284 , 1.1445 ,0.006865 ,0.006865 ,0.006865
+ 192 , 1.3666 , 1.3074 , 1.3286 , 0.79386 , 0.61998 , 0.72084 , 0.48909 , 1.1154 , 0.89148 , 1.1438 ,0.0068485 ,0.0068485 ,0.0068485
+ 193 , 1.3657 , 1.3049 , 1.33 , 0.79661 , 0.61826 , 0.7211 , 0.48946 , 1.1145 , 0.89053 , 1.1429 ,0.006832 ,0.006832 ,0.006832
+ 194 , 1.3627 , 1.3032 , 1.327 , 0.79668 , 0.61854 , 0.72145 , 0.49001 , 1.1137 , 0.89026 , 1.1421 ,0.0068155 ,0.0068155 ,0.0068155
+ 195 , 1.3637 , 1.3033 , 1.3276 , 0.79737 , 0.61855 , 0.72124 , 0.48971 , 1.1141 , 0.89116 , 1.1417 ,0.006799 ,0.006799 ,0.006799
+ 196 , 1.3639 , 1.3009 , 1.325 , 0.80029 , 0.61826 , 0.72141 , 0.49007 , 1.1136 , 0.89224 , 1.1412 ,0.0067825 ,0.0067825 ,0.0067825
+ 197 , 1.3626 , 1.2983 , 1.3269 , 0.80203 , 0.61548 , 0.72112 , 0.49013 , 1.1129 , 0.89168 , 1.1404 ,0.006766 ,0.006766 ,0.006766
+ 198 , 1.364 , 1.2984 , 1.3278 , 0.80286 , 0.61597 , 0.72174 , 0.49004 , 1.1133 , 0.89091 , 1.1407 ,0.0067495 ,0.0067495 ,0.0067495
+ 199 , 1.3622 , 1.2985 , 1.3247 , 0.79939 , 0.61882 , 0.72181 , 0.49031 , 1.1134 , 0.89116 , 1.1404 ,0.006733 ,0.006733 ,0.006733
+ 200 , 1.3645 , 1.3016 , 1.3266 , 0.80107 , 0.61766 , 0.72176 , 0.49053 , 1.1134 , 0.89021 , 1.1401 ,0.0067165 ,0.0067165 ,0.0067165
+ 201 , 1.3646 , 1.3041 , 1.3265 , 0.79653 , 0.61788 , 0.72181 , 0.49082 , 1.1133 , 0.89033 , 1.1399 ,0.0067 ,0.0067 ,0.0067
+ 202 , 1.364 , 1.2989 , 1.3247 , 0.80013 , 0.61739 , 0.72209 , 0.49037 , 1.1138 , 0.88978 , 1.14 ,0.0066835 ,0.0066835 ,0.0066835
+ 203 , 1.362 , 1.3014 , 1.3235 , 0.79777 , 0.61937 , 0.72169 , 0.49034 , 1.1147 , 0.88991 , 1.1407 ,0.006667 ,0.006667 ,0.006667
+ 204 , 1.3631 , 1.2982 , 1.326 , 0.796 , 0.62179 , 0.72215 , 0.49057 , 1.1143 , 0.88973 , 1.1407 ,0.0066505 ,0.0066505 ,0.0066505
+ 205 , 1.3634 , 1.3025 , 1.3243 , 0.80104 , 0.619 , 0.72249 , 0.49123 , 1.1137 , 0.88861 , 1.1404 ,0.006634 ,0.006634 ,0.006634
+ 206 , 1.3579 , 1.2995 , 1.3248 , 0.80012 , 0.62142 , 0.72309 , 0.49123 , 1.1122 , 0.88847 , 1.14 ,0.0066175 ,0.0066175 ,0.0066175
+ 207 , 1.3615 , 1.3008 , 1.3237 , 0.8026 , 0.61937 , 0.72321 , 0.49141 , 1.1111 , 0.88745 , 1.139 ,0.006601 ,0.006601 ,0.006601
+ 208 , 1.3616 , 1.3005 , 1.3223 , 0.80217 , 0.6203 , 0.72357 , 0.49194 , 1.1104 , 0.88743 , 1.1379 ,0.0065845 ,0.0065845 ,0.0065845
+ 209 , 1.3588 , 1.2939 , 1.32 , 0.79866 , 0.62313 , 0.72384 , 0.49189 , 1.1103 , 0.88626 , 1.1379 ,0.006568 ,0.006568 ,0.006568
+ 210 , 1.357 , 1.2967 , 1.3204 , 0.80215 , 0.62104 , 0.72368 , 0.4921 , 1.11 , 0.88667 , 1.1377 ,0.0065515 ,0.0065515 ,0.0065515
+ 211 , 1.3575 , 1.2971 , 1.3191 , 0.80469 , 0.6204 , 0.72402 , 0.49219 , 1.1104 , 0.88736 , 1.1379 ,0.006535 ,0.006535 ,0.006535
+ 212 , 1.3534 , 1.2933 , 1.3177 , 0.80667 , 0.62101 , 0.72456 , 0.49235 , 1.1103 , 0.88666 , 1.1374 ,0.0065185 ,0.0065185 ,0.0065185
+ 213 , 1.3579 , 1.2926 , 1.3187 , 0.80181 , 0.62374 , 0.72414 , 0.49209 , 1.1096 , 0.88623 , 1.137 ,0.006502 ,0.006502 ,0.006502
+ 214 , 1.3606 , 1.299 , 1.3192 , 0.80295 , 0.62383 , 0.72384 , 0.49157 , 1.1093 , 0.88726 , 1.1365 ,0.0064855 ,0.0064855 ,0.0064855
+ 215 , 1.3618 , 1.2983 , 1.3203 , 0.79935 , 0.62289 , 0.72321 , 0.49169 , 1.1087 , 0.88802 , 1.1364 ,0.006469 ,0.006469 ,0.006469
+ 216 , 1.3567 , 1.2914 , 1.3178 , 0.79862 , 0.62114 , 0.72244 , 0.49208 , 1.1076 , 0.88755 , 1.1352 ,0.0064525 ,0.0064525 ,0.0064525
+ 217 , 1.3591 , 1.293 , 1.3163 , 0.7968 , 0.62299 , 0.72278 , 0.492 , 1.1072 , 0.88672 , 1.1342 ,0.006436 ,0.006436 ,0.006436
+ 218 , 1.3537 , 1.2901 , 1.3157 , 0.79663 , 0.62457 , 0.72327 , 0.49188 , 1.1081 , 0.88686 , 1.1345 ,0.0064195 ,0.0064195 ,0.0064195
+ 219 , 1.354 , 1.2908 , 1.319 , 0.80091 , 0.62263 , 0.72301 , 0.49205 , 1.1084 , 0.88723 , 1.135 ,0.006403 ,0.006403 ,0.006403
+ 220 , 1.3575 , 1.295 , 1.3192 , 0.79993 , 0.62142 , 0.72321 , 0.49238 , 1.108 , 0.88634 , 1.1348 ,0.0063865 ,0.0063865 ,0.0063865
+ 221 , 1.3612 , 1.2929 , 1.3177 , 0.79543 , 0.62311 , 0.72313 , 0.49289 , 1.1077 , 0.88638 , 1.1349 ,0.00637 ,0.00637 ,0.00637
+ 222 , 1.3558 , 1.2926 , 1.3182 , 0.79728 , 0.62404 , 0.72345 , 0.49273 , 1.1075 , 0.88606 , 1.1348 ,0.0063535 ,0.0063535 ,0.0063535
+ 223 , 1.3551 , 1.2931 , 1.3182 , 0.802 , 0.6216 , 0.724 , 0.49299 , 1.1065 , 0.88525 , 1.1337 ,0.006337 ,0.006337 ,0.006337
+ 224 , 1.3547 , 1.2916 , 1.3155 , 0.80634 , 0.61947 , 0.72441 , 0.49331 , 1.1053 , 0.88365 , 1.1325 ,0.0063205 ,0.0063205 ,0.0063205
+ 225 , 1.3566 , 1.2916 , 1.3158 , 0.80581 , 0.61975 , 0.72532 , 0.49375 , 1.1056 , 0.88254 , 1.1327 ,0.006304 ,0.006304 ,0.006304
+ 226 , 1.3541 , 1.2875 , 1.3155 , 0.80477 , 0.61811 , 0.72558 , 0.4938 , 1.1048 , 0.88176 , 1.1324 ,0.0062875 ,0.0062875 ,0.0062875
+ 227 , 1.3567 , 1.2915 , 1.3144 , 0.80358 , 0.61914 , 0.72493 , 0.49362 , 1.1045 , 0.88105 , 1.1324 ,0.006271 ,0.006271 ,0.006271
+ 228 , 1.3541 , 1.2867 , 1.3142 , 0.80268 , 0.61943 , 0.72532 , 0.49383 , 1.1047 , 0.88005 , 1.1328 ,0.0062545 ,0.0062545 ,0.0062545
+ 229 , 1.3503 , 1.2861 , 1.3128 , 0.80303 , 0.62012 , 0.72527 , 0.49402 , 1.1036 , 0.87929 , 1.1318 ,0.006238 ,0.006238 ,0.006238
+ 230 , 1.3512 , 1.2855 , 1.3138 , 0.804 , 0.61863 , 0.72525 , 0.49451 , 1.1037 , 0.87903 , 1.132 ,0.0062215 ,0.0062215 ,0.0062215
+ 231 , 1.3525 , 1.2884 , 1.3147 , 0.79818 , 0.62234 , 0.72553 , 0.49449 , 1.1035 , 0.87941 , 1.1316 ,0.006205 ,0.006205 ,0.006205
+ 232 , 1.3515 , 1.2841 , 1.3135 , 0.79395 , 0.62534 , 0.72513 , 0.49412 , 1.1048 , 0.88089 , 1.1317 ,0.0061885 ,0.0061885 ,0.0061885
+ 233 , 1.3499 , 1.2868 , 1.3143 , 0.80167 , 0.62216 , 0.7255 , 0.49421 , 1.1051 , 0.88114 , 1.1323 ,0.006172 ,0.006172 ,0.006172
+ 234 , 1.3511 , 1.2835 , 1.3123 , 0.80214 , 0.62049 , 0.72575 , 0.49454 , 1.1044 , 0.88109 , 1.1311 ,0.0061555 ,0.0061555 ,0.0061555
+ 235 , 1.3481 , 1.285 , 1.3108 , 0.8015 , 0.62169 , 0.7256 , 0.49444 , 1.1048 , 0.88139 , 1.1309 ,0.006139 ,0.006139 ,0.006139
+ 236 , 1.3494 , 1.2808 , 1.3114 , 0.80111 , 0.62117 , 0.72497 , 0.49453 , 1.104 , 0.88211 , 1.1303 ,0.0061225 ,0.0061225 ,0.0061225
+ 237 , 1.3498 , 1.2827 , 1.3097 , 0.80459 , 0.61937 , 0.72472 , 0.49433 , 1.1027 , 0.88203 , 1.1294 ,0.006106 ,0.006106 ,0.006106
+ 238 , 1.3485 , 1.2836 , 1.3103 , 0.80525 , 0.62169 , 0.72541 , 0.49512 , 1.1026 , 0.88167 , 1.1291 ,0.0060895 ,0.0060895 ,0.0060895
+ 239 , 1.3507 , 1.2834 , 1.3103 , 0.80418 , 0.62191 , 0.72568 , 0.49526 , 1.1029 , 0.88083 , 1.1296 ,0.006073 ,0.006073 ,0.006073
+ 240 , 1.3484 , 1.2831 , 1.3095 , 0.80371 , 0.62044 , 0.72508 , 0.49509 , 1.1032 , 0.8815 , 1.1298 ,0.0060565 ,0.0060565 ,0.0060565
+ 241 , 1.3502 , 1.2813 , 1.3094 , 0.809 , 0.61956 , 0.72617 , 0.49493 , 1.1038 , 0.88036 , 1.1299 ,0.00604 ,0.00604 ,0.00604
+ 242 , 1.346 , 1.2782 , 1.308 , 0.80323 , 0.62119 , 0.72556 , 0.49491 , 1.1035 , 0.88062 , 1.1295 ,0.0060235 ,0.0060235 ,0.0060235
+ 243 , 1.3474 , 1.2772 , 1.307 , 0.79861 , 0.62327 , 0.72558 , 0.49531 , 1.1023 , 0.8795 , 1.1282 ,0.006007 ,0.006007 ,0.006007
+ 244 , 1.3497 , 1.2834 , 1.3108 , 0.7976 , 0.62457 , 0.72686 , 0.4955 , 1.1012 , 0.8789 , 1.128 ,0.0059905 ,0.0059905 ,0.0059905
+ 245 , 1.3469 , 1.2802 , 1.3061 , 0.80437 , 0.62142 , 0.72632 , 0.49546 , 1.1016 , 0.87929 , 1.1286 ,0.005974 ,0.005974 ,0.005974
+ 246 , 1.3445 , 1.2781 , 1.3071 , 0.79698 , 0.62616 , 0.7267 , 0.49591 , 1.1022 , 0.87889 , 1.1293 ,0.0059575 ,0.0059575 ,0.0059575
+ 247 , 1.3465 , 1.2804 , 1.3092 , 0.80271 , 0.62181 , 0.7272 , 0.49601 , 1.1019 , 0.87871 , 1.1289 ,0.005941 ,0.005941 ,0.005941
+ 248 , 1.3456 , 1.2772 , 1.3071 , 0.80238 , 0.622 , 0.72719 , 0.49628 , 1.1008 , 0.87755 , 1.128 ,0.0059245 ,0.0059245 ,0.0059245
+ 249 , 1.3481 , 1.2834 , 1.308 , 0.79994 , 0.6216 , 0.72665 , 0.49646 , 1.1008 , 0.87678 , 1.1275 ,0.005908 ,0.005908 ,0.005908
+ 250 , 1.3439 , 1.2799 , 1.3053 , 0.7988 , 0.62346 , 0.72712 , 0.49613 , 1.1012 , 0.87634 , 1.1274 ,0.0058915 ,0.0058915 ,0.0058915
+ 251 , 1.346 , 1.2759 , 1.3045 , 0.80096 , 0.62321 , 0.72703 , 0.49602 , 1.1018 , 0.87684 , 1.1276 ,0.005875 ,0.005875 ,0.005875
+ 252 , 1.3476 , 1.2796 , 1.3081 , 0.7977 , 0.62336 , 0.72683 , 0.4962 , 1.1029 , 0.87789 , 1.1276 ,0.0058585 ,0.0058585 ,0.0058585
+ 253 , 1.3429 , 1.2786 , 1.3055 , 0.79756 , 0.62541 , 0.72704 , 0.49649 , 1.1022 , 0.87746 , 1.1276 ,0.005842 ,0.005842 ,0.005842
+ 254 , 1.3438 , 1.2756 , 1.3043 , 0.80111 , 0.62401 , 0.72689 , 0.49607 , 1.1025 , 0.87688 , 1.1276 ,0.0058255 ,0.0058255 ,0.0058255
+ 255 , 1.3466 , 1.2753 , 1.3078 , 0.79667 , 0.6268 , 0.72685 , 0.49607 , 1.1033 , 0.87642 , 1.1279 ,0.005809 ,0.005809 ,0.005809
+ 256 , 1.3449 , 1.2763 , 1.3047 , 0.79898 , 0.62466 , 0.72606 , 0.49573 , 1.103 , 0.87705 , 1.1278 ,0.0057925 ,0.0057925 ,0.0057925
+ 257 , 1.3434 , 1.2738 , 1.3047 , 0.80137 , 0.62368 , 0.72631 , 0.49594 , 1.1035 , 0.87732 , 1.128 ,0.005776 ,0.005776 ,0.005776
+ 258 , 1.343 , 1.2734 , 1.304 , 0.79944 , 0.62285 , 0.7262 , 0.49553 , 1.1042 , 0.87756 , 1.1283 ,0.0057595 ,0.0057595 ,0.0057595
+ 259 , 1.3432 , 1.2744 , 1.3031 , 0.80022 , 0.62181 , 0.72627 , 0.49598 , 1.1044 , 0.87766 , 1.1287 ,0.005743 ,0.005743 ,0.005743
+ 260 , 1.3432 , 1.2722 , 1.3028 , 0.79474 , 0.62334 , 0.72627 , 0.49597 , 1.1046 , 0.87721 , 1.1281 ,0.0057265 ,0.0057265 ,0.0057265
+ 261 , 1.3436 , 1.2744 , 1.3042 , 0.79544 , 0.62315 , 0.72638 , 0.49627 , 1.1042 , 0.87654 , 1.1275 ,0.00571 ,0.00571 ,0.00571
+ 262 , 1.3414 , 1.2713 , 1.3019 , 0.79972 , 0.62135 , 0.72669 , 0.49628 , 1.1052 , 0.87592 , 1.1283 ,0.0056935 ,0.0056935 ,0.0056935
+ 263 , 1.3404 , 1.273 , 1.3018 , 0.79727 , 0.62457 , 0.72713 , 0.49638 , 1.1042 , 0.87465 , 1.1277 ,0.005677 ,0.005677 ,0.005677
+ 264 , 1.3404 , 1.2732 , 1.303 , 0.79797 , 0.62378 , 0.72778 , 0.49702 , 1.1034 , 0.87395 , 1.1268 ,0.0056605 ,0.0056605 ,0.0056605
+ 265 , 1.3374 , 1.2666 , 1.2996 , 0.79744 , 0.62336 , 0.72751 , 0.49727 , 1.1025 , 0.87364 , 1.1264 ,0.005644 ,0.005644 ,0.005644
+ 266 , 1.3404 , 1.2687 , 1.3019 , 0.79347 , 0.62745 , 0.72772 , 0.497 , 1.1027 , 0.8728 , 1.1268 ,0.0056275 ,0.0056275 ,0.0056275
+ 267 , 1.3394 , 1.273 , 1.3044 , 0.79565 , 0.62522 , 0.7277 , 0.49671 , 1.1024 , 0.87257 , 1.1264 ,0.005611 ,0.005611 ,0.005611
+ 268 , 1.3429 , 1.2742 , 1.3041 , 0.79687 , 0.62522 , 0.72761 , 0.49701 , 1.102 , 0.87286 , 1.1262 ,0.0055945 ,0.0055945 ,0.0055945
+ 269 , 1.3408 , 1.2706 , 1.3022 , 0.8029 , 0.62216 , 0.72737 , 0.49696 , 1.1012 , 0.8729 , 1.1264 ,0.005578 ,0.005578 ,0.005578
+ 270 , 1.3393 , 1.2686 , 1.299 , 0.79937 , 0.62517 , 0.72749 , 0.49695 , 1.1006 , 0.87273 , 1.1254 ,0.0055615 ,0.0055615 ,0.0055615
+ 271 , 1.3376 , 1.2654 , 1.2999 , 0.79513 , 0.62697 , 0.72783 , 0.49691 , 1.1006 , 0.87332 , 1.1254 ,0.005545 ,0.005545 ,0.005545
+ 272 , 1.3374 , 1.2682 , 1.2999 , 0.79143 , 0.6299 , 0.72876 , 0.49805 , 1.0998 , 0.87293 , 1.1253 ,0.0055285 ,0.0055285 ,0.0055285
+ 273 , 1.3377 , 1.2695 , 1.2983 , 0.8008 , 0.6242 , 0.72835 , 0.49758 , 1.1004 , 0.87239 , 1.1254 ,0.005512 ,0.005512 ,0.005512
+ 274 , 1.3386 , 1.2667 , 1.2976 , 0.80123 , 0.62207 , 0.7284 , 0.49793 , 1.1009 , 0.87112 , 1.1247 ,0.0054955 ,0.0054955 ,0.0054955
+ 275 , 1.3368 , 1.2675 , 1.2982 , 0.80321 , 0.61993 , 0.7283 , 0.49788 , 1.1002 , 0.87177 , 1.1238 ,0.005479 ,0.005479 ,0.005479
+ 276 , 1.3367 , 1.2645 , 1.2987 , 0.8037 , 0.62189 , 0.72903 , 0.49851 , 1.1 , 0.87067 , 1.1232 ,0.0054625 ,0.0054625 ,0.0054625
+ 277 , 1.3348 , 1.2642 , 1.2981 , 0.79821 , 0.62494 , 0.72902 , 0.49896 , 1.0991 , 0.87053 , 1.1227 ,0.005446 ,0.005446 ,0.005446
+ 278 , 1.3351 , 1.2658 , 1.2988 , 0.80124 , 0.62411 , 0.72931 , 0.49897 , 1.0994 , 0.87112 , 1.1232 ,0.0054295 ,0.0054295 ,0.0054295
+ 279 , 1.3352 , 1.2616 , 1.298 , 0.79798 , 0.62559 , 0.72943 , 0.49916 , 1.0991 , 0.87089 , 1.1229 ,0.005413 ,0.005413 ,0.005413
+ 280 , 1.333 , 1.2581 , 1.2952 , 0.79671 , 0.6281 , 0.72957 , 0.4994 , 1.0988 , 0.87041 , 1.1222 ,0.0053965 ,0.0053965 ,0.0053965
+ 281 , 1.3347 , 1.2657 , 1.3004 , 0.80105 , 0.62763 , 0.72953 , 0.49896 , 1.0984 , 0.86961 , 1.122 ,0.00538 ,0.00538 ,0.00538
+ 282 , 1.3334 , 1.263 , 1.2979 , 0.79643 , 0.62884 , 0.72953 , 0.49914 , 1.0983 , 0.86866 , 1.1218 ,0.0053635 ,0.0053635 ,0.0053635
+ 283 , 1.3329 , 1.2616 , 1.2963 , 0.7996 , 0.6279 , 0.7298 , 0.4987 , 1.0983 , 0.86929 , 1.1219 ,0.005347 ,0.005347 ,0.005347
+ 284 , 1.3345 , 1.2624 , 1.2979 , 0.79654 , 0.62856 , 0.72928 , 0.49889 , 1.0991 , 0.8698 , 1.1222 ,0.0053305 ,0.0053305 ,0.0053305
+ 285 , 1.3323 , 1.2609 , 1.2953 , 0.79879 , 0.62735 , 0.7295 , 0.49884 , 1.0986 , 0.86982 , 1.1217 ,0.005314 ,0.005314 ,0.005314
+ 286 , 1.3334 , 1.2612 , 1.2962 , 0.7973 , 0.6296 , 0.73038 , 0.49883 , 1.0974 , 0.87028 , 1.1205 ,0.0052975 ,0.0052975 ,0.0052975
+ 287 , 1.3315 , 1.2575 , 1.2936 , 0.79906 , 0.62874 , 0.72945 , 0.49892 , 1.0973 , 0.87058 , 1.1207 ,0.005281 ,0.005281 ,0.005281
+ 288 , 1.3351 , 1.2596 , 1.2949 , 0.79753 , 0.63134 , 0.72997 , 0.49898 , 1.098 , 0.86995 , 1.1213 ,0.0052645 ,0.0052645 ,0.0052645
+ 289 , 1.3324 , 1.26 , 1.294 , 0.80092 , 0.62865 , 0.73061 , 0.49929 , 1.0981 , 0.87034 , 1.1211 ,0.005248 ,0.005248 ,0.005248
+ 290 , 1.3305 , 1.2573 , 1.2945 , 0.80158 , 0.63051 , 0.73041 , 0.49928 , 1.0983 , 0.86934 , 1.1213 ,0.0052315 ,0.0052315 ,0.0052315
+ 291 , 1.3302 , 1.2579 , 1.2947 , 0.79899 , 0.63032 , 0.73016 , 0.49952 , 1.0978 , 0.86993 , 1.1214 ,0.005215 ,0.005215 ,0.005215
+ 292 , 1.3273 , 1.2533 , 1.2916 , 0.80257 , 0.62791 , 0.72979 , 0.49918 , 1.0976 , 0.87026 , 1.1213 ,0.0051985 ,0.0051985 ,0.0051985
+ 293 , 1.3288 , 1.2528 , 1.2936 , 0.79947 , 0.62815 , 0.73028 , 0.49918 , 1.0975 , 0.86939 , 1.1217 ,0.005182 ,0.005182 ,0.005182
+ 294 , 1.331 , 1.2573 , 1.2951 , 0.79803 , 0.62633 , 0.72937 , 0.49921 , 1.0967 , 0.86821 , 1.1215 ,0.0051655 ,0.0051655 ,0.0051655
+ 295 , 1.3304 , 1.26 , 1.2947 , 0.80079 , 0.62522 , 0.72935 , 0.49923 , 1.0965 , 0.86918 , 1.1203 ,0.005149 ,0.005149 ,0.005149
+ 296 , 1.3286 , 1.2552 , 1.2927 , 0.79793 , 0.62754 , 0.7293 , 0.4991 , 1.0961 , 0.86868 , 1.12 ,0.0051325 ,0.0051325 ,0.0051325
+ 297 , 1.3299 , 1.2535 , 1.2927 , 0.79751 , 0.62884 , 0.72998 , 0.49945 , 1.0951 , 0.86844 , 1.1193 ,0.005116 ,0.005116 ,0.005116
+ 298 , 1.3284 , 1.2572 , 1.2956 , 0.79749 , 0.62949 , 0.72978 , 0.49977 , 1.0963 , 0.86816 , 1.1205 ,0.0050995 ,0.0050995 ,0.0050995
+ 299 , 1.3271 , 1.2543 , 1.2919 , 0.79498 , 0.63218 , 0.73013 , 0.50006 , 1.0966 , 0.86754 , 1.1207 ,0.005083 ,0.005083 ,0.005083
+ 300 , 1.3284 , 1.2515 , 1.2911 , 0.79323 , 0.63097 , 0.72994 , 0.49932 , 1.0968 , 0.86693 , 1.1206 ,0.0050665 ,0.0050665 ,0.0050665
+ 301 , 1.3265 , 1.2523 , 1.2943 , 0.79457 , 0.63246 , 0.73041 , 0.50004 , 1.0958 , 0.86599 , 1.1196 ,0.00505 ,0.00505 ,0.00505
+ 302 , 1.3291 , 1.2561 , 1.2917 , 0.79161 , 0.63306 , 0.7293 , 0.49983 , 1.0957 , 0.86611 , 1.1195 ,0.0050335 ,0.0050335 ,0.0050335
+ 303 , 1.3303 , 1.2557 , 1.2909 , 0.79335 , 0.63422 , 0.72973 , 0.49946 , 1.0939 , 0.86606 , 1.1179 ,0.005017 ,0.005017 ,0.005017
+ 304 , 1.3264 , 1.249 , 1.2893 , 0.79251 , 0.63422 , 0.73035 , 0.50014 , 1.0938 , 0.86627 , 1.1177 ,0.0050005 ,0.0050005 ,0.0050005
+ 305 , 1.3278 , 1.2543 , 1.2913 , 0.79321 , 0.63292 , 0.73071 , 0.5006 , 1.0929 , 0.86589 , 1.1171 ,0.004984 ,0.004984 ,0.004984
+ 306 , 1.3276 , 1.2528 , 1.2915 , 0.79197 , 0.63459 , 0.73102 , 0.50042 , 1.0921 , 0.86492 , 1.1169 ,0.0049675 ,0.0049675 ,0.0049675
+ 307 , 1.3255 , 1.25 , 1.2902 , 0.80121 , 0.62921 , 0.73096 , 0.50068 , 1.0924 , 0.86386 , 1.117 ,0.004951 ,0.004951 ,0.004951
+ 308 , 1.3264 , 1.2508 , 1.2882 , 0.80252 , 0.62972 , 0.73186 , 0.50094 , 1.0919 , 0.86396 , 1.116 ,0.0049345 ,0.0049345 ,0.0049345
+ 309 , 1.3279 , 1.2494 , 1.2883 , 0.80643 , 0.62894 , 0.73204 , 0.50148 , 1.0917 , 0.86413 , 1.1161 ,0.004918 ,0.004918 ,0.004918
+ 310 , 1.3242 , 1.2468 , 1.2871 , 0.80502 , 0.62708 , 0.73151 , 0.50102 , 1.0915 , 0.86471 , 1.116 ,0.0049015 ,0.0049015 ,0.0049015
+ 311 , 1.3264 , 1.2491 , 1.2895 , 0.80126 , 0.63222 , 0.73196 , 0.50186 , 1.0906 , 0.86335 , 1.1155 ,0.004885 ,0.004885 ,0.004885
+ 312 , 1.3239 , 1.2472 , 1.2871 , 0.80095 , 0.63325 , 0.73166 , 0.50179 , 1.0904 , 0.86285 , 1.1153 ,0.0048685 ,0.0048685 ,0.0048685
+ 313 , 1.3244 , 1.2462 , 1.2871 , 0.79827 , 0.63366 , 0.73193 , 0.50165 , 1.0906 , 0.86247 , 1.1154 ,0.004852 ,0.004852 ,0.004852
+ 314 , 1.3229 , 1.2439 , 1.286 , 0.80199 , 0.63032 , 0.73147 , 0.50141 , 1.09 , 0.86233 , 1.1145 ,0.0048355 ,0.0048355 ,0.0048355
+ 315 , 1.3253 , 1.2481 , 1.2884 , 0.8032 , 0.62865 , 0.73245 , 0.50149 , 1.0899 , 0.86188 , 1.1143 ,0.004819 ,0.004819 ,0.004819
+ 316 , 1.3234 , 1.245 , 1.2862 , 0.79952 , 0.63218 , 0.73247 , 0.50167 , 1.0885 , 0.86137 , 1.113 ,0.0048025 ,0.0048025 ,0.0048025
+ 317 , 1.3208 , 1.2448 , 1.2846 , 0.80105 , 0.63213 , 0.73246 , 0.50167 , 1.0883 , 0.86201 , 1.1127 ,0.004786 ,0.004786 ,0.004786
+ 318 , 1.3229 , 1.245 , 1.287 , 0.79523 , 0.6346 , 0.73244 , 0.50156 , 1.0876 , 0.86184 , 1.1119 ,0.0047695 ,0.0047695 ,0.0047695
+ 319 , 1.3234 , 1.2469 , 1.2857 , 0.79962 , 0.63317 , 0.73228 , 0.5023 , 1.0883 , 0.86264 , 1.1125 ,0.004753 ,0.004753 ,0.004753
+ 320 , 1.3218 , 1.2438 , 1.2863 , 0.79564 , 0.63515 , 0.7329 , 0.50234 , 1.0886 , 0.863 , 1.1128 ,0.0047365 ,0.0047365 ,0.0047365
+ 321 , 1.3218 , 1.2415 , 1.2847 , 0.80069 , 0.63221 , 0.73196 , 0.50254 , 1.0873 , 0.86286 , 1.1121 ,0.00472 ,0.00472 ,0.00472
+ 322 , 1.3187 , 1.2428 , 1.2837 , 0.80188 , 0.63255 , 0.73204 , 0.50288 , 1.0857 , 0.86195 , 1.1109 ,0.0047035 ,0.0047035 ,0.0047035
+ 323 , 1.3209 , 1.2418 , 1.2866 , 0.80113 , 0.63181 , 0.73205 , 0.5024 , 1.085 , 0.86215 , 1.11 ,0.004687 ,0.004687 ,0.004687
+ 324 , 1.3147 , 1.2378 , 1.2832 , 0.80154 , 0.63223 , 0.73249 , 0.50229 , 1.0855 , 0.86207 , 1.1108 ,0.0046705 ,0.0046705 ,0.0046705
+ 325 , 1.3181 , 1.2413 , 1.2854 , 0.80218 , 0.63069 , 0.73211 , 0.50244 , 1.0866 , 0.86165 , 1.1118 ,0.004654 ,0.004654 ,0.004654
+ 326 , 1.3197 , 1.2403 , 1.2852 , 0.79987 , 0.63119 , 0.73214 , 0.50251 , 1.0874 , 0.86046 , 1.1125 ,0.0046375 ,0.0046375 ,0.0046375
+ 327 , 1.3151 , 1.2367 , 1.2817 , 0.79755 , 0.63301 , 0.73236 , 0.50238 , 1.0878 , 0.8611 , 1.113 ,0.004621 ,0.004621 ,0.004621
+ 328 , 1.3166 , 1.2392 , 1.2825 , 0.79487 , 0.63459 , 0.7322 , 0.50236 , 1.0881 , 0.86099 , 1.1131 ,0.0046045 ,0.0046045 ,0.0046045
+ 329 , 1.3192 , 1.2396 , 1.2842 , 0.79673 , 0.63524 , 0.73266 , 0.50362 , 1.0879 , 0.86083 , 1.1128 ,0.004588 ,0.004588 ,0.004588
+ 330 , 1.3187 , 1.2401 , 1.2842 , 0.8007 , 0.63298 , 0.73333 , 0.50411 , 1.0869 , 0.86046 , 1.112 ,0.0045715 ,0.0045715 ,0.0045715
+ 331 , 1.3173 , 1.2385 , 1.2839 , 0.80242 , 0.63246 , 0.73361 , 0.50414 , 1.0869 , 0.8602 , 1.112 ,0.004555 ,0.004555 ,0.004555
+ 332 , 1.3167 , 1.238 , 1.2842 , 0.80579 , 0.63097 , 0.73354 , 0.50443 , 1.0861 , 0.86065 , 1.1116 ,0.0045385 ,0.0045385 ,0.0045385
+ 333 , 1.3162 , 1.2377 , 1.2831 , 0.80217 , 0.63361 , 0.73307 , 0.50463 , 1.0858 , 0.8611 , 1.1114 ,0.004522 ,0.004522 ,0.004522
+ 334 , 1.3157 , 1.236 , 1.2819 , 0.8041 , 0.63237 , 0.73379 , 0.5047 , 1.0858 , 0.86126 , 1.1116 ,0.0045055 ,0.0045055 ,0.0045055
+ 335 , 1.3169 , 1.2379 , 1.2826 , 0.80127 , 0.6334 , 0.73325 , 0.50408 , 1.0854 , 0.86087 , 1.1113 ,0.004489 ,0.004489 ,0.004489
+ 336 , 1.3158 , 1.2371 , 1.2828 , 0.80261 , 0.6332 , 0.73339 , 0.50409 , 1.0845 , 0.86034 , 1.1108 ,0.0044725 ,0.0044725 ,0.0044725
+ 337 , 1.3153 , 1.236 , 1.2813 , 0.80384 , 0.63237 , 0.73374 , 0.50396 , 1.0845 , 0.86012 , 1.1105 ,0.004456 ,0.004456 ,0.004456
+ 338 , 1.3157 , 1.2364 , 1.2811 , 0.80352 , 0.63257 , 0.73415 , 0.50447 , 1.0839 , 0.85917 , 1.1104 ,0.0044395 ,0.0044395 ,0.0044395
+ 339 , 1.3161 , 1.2367 , 1.281 , 0.80617 , 0.6306 , 0.73427 , 0.50445 , 1.0842 , 0.85819 , 1.11 ,0.004423 ,0.004423 ,0.004423
+ 340 , 1.312 , 1.2337 , 1.2792 , 0.80915 , 0.63014 , 0.73426 , 0.50438 , 1.0835 , 0.85832 , 1.1095 ,0.0044065 ,0.0044065 ,0.0044065
+ 341 , 1.3117 , 1.2347 , 1.2796 , 0.81032 , 0.62871 , 0.7348 , 0.50447 , 1.0836 , 0.85673 , 1.1092 ,0.00439 ,0.00439 ,0.00439
+ 342 , 1.3139 , 1.2335 , 1.2816 , 0.80842 , 0.63001 , 0.73516 , 0.5052 , 1.0832 , 0.85588 , 1.1091 ,0.0043735 ,0.0043735 ,0.0043735
+ 343 , 1.3137 , 1.2332 , 1.2815 , 0.81312 , 0.62875 , 0.7353 , 0.50609 , 1.0831 , 0.85531 , 1.109 ,0.004357 ,0.004357 ,0.004357
+ 344 , 1.3133 , 1.2341 , 1.2821 , 0.80884 , 0.63079 , 0.73523 , 0.50574 , 1.0836 , 0.85539 , 1.1092 ,0.0043405 ,0.0043405 ,0.0043405
+ 345 , 1.3118 , 1.2302 , 1.2801 , 0.80925 , 0.63125 , 0.73524 , 0.50553 , 1.0842 , 0.85523 , 1.11 ,0.004324 ,0.004324 ,0.004324
+ 346 , 1.3131 , 1.2328 , 1.2797 , 0.80846 , 0.63051 , 0.73535 , 0.50557 , 1.0845 , 0.85539 , 1.1103 ,0.0043075 ,0.0043075 ,0.0043075
+ 347 , 1.3115 , 1.2314 , 1.2776 , 0.80656 , 0.63217 , 0.73578 , 0.50631 , 1.0851 , 0.85529 , 1.1104 ,0.004291 ,0.004291 ,0.004291
+ 348 , 1.3086 , 1.2243 , 1.2775 , 0.80421 , 0.63385 , 0.73617 , 0.50624 , 1.084 , 0.85544 , 1.1096 ,0.0042745 ,0.0042745 ,0.0042745
+ 349 , 1.3078 , 1.2247 , 1.2758 , 0.80089 , 0.63533 , 0.7364 , 0.50607 , 1.0829 , 0.85441 , 1.1092 ,0.004258 ,0.004258 ,0.004258
+ 350 , 1.3093 , 1.2277 , 1.2767 , 0.80215 , 0.63315 , 0.73641 , 0.50667 , 1.0817 , 0.85376 , 1.108 ,0.0042415 ,0.0042415 ,0.0042415
+ 351 , 1.3114 , 1.2267 , 1.2791 , 0.8043 , 0.63255 , 0.73664 , 0.50644 , 1.0826 , 0.85382 , 1.1078 ,0.004225 ,0.004225 ,0.004225
+ 352 , 1.3107 , 1.2284 , 1.2764 , 0.80021 , 0.63673 , 0.73599 , 0.50653 , 1.0826 , 0.85386 , 1.1079 ,0.0042085 ,0.0042085 ,0.0042085
+ 353 , 1.3051 , 1.2231 , 1.2743 , 0.79365 , 0.63846 , 0.73588 , 0.50612 , 1.0828 , 0.85351 , 1.1078 ,0.004192 ,0.004192 ,0.004192
+ 354 , 1.3081 , 1.2263 , 1.2772 , 0.79274 , 0.6384 , 0.73656 , 0.50619 , 1.0836 , 0.85374 , 1.1083 ,0.0041755 ,0.0041755 ,0.0041755
+ 355 , 1.3089 , 1.2272 , 1.2754 , 0.79932 , 0.6346 , 0.73631 , 0.50592 , 1.0833 , 0.85242 , 1.1081 ,0.004159 ,0.004159 ,0.004159
+ 356 , 1.3111 , 1.2288 , 1.2764 , 0.80104 , 0.63431 , 0.7369 , 0.50593 , 1.0839 , 0.85178 , 1.108 ,0.0041425 ,0.0041425 ,0.0041425
+ 357 , 1.3079 , 1.2247 , 1.2757 , 0.80314 , 0.63478 , 0.73636 , 0.50585 , 1.0843 , 0.85214 , 1.1077 ,0.004126 ,0.004126 ,0.004126
+ 358 , 1.3076 , 1.2248 , 1.2733 , 0.80359 , 0.6355 , 0.73619 , 0.50586 , 1.0842 , 0.85228 , 1.1076 ,0.0041095 ,0.0041095 ,0.0041095
+ 359 , 1.3092 , 1.2263 , 1.2737 , 0.80368 , 0.63422 , 0.73587 , 0.50582 , 1.0835 , 0.85093 , 1.1077 ,0.004093 ,0.004093 ,0.004093
+ 360 , 1.3089 , 1.224 , 1.2751 , 0.80347 , 0.63339 , 0.73608 , 0.50611 , 1.0843 , 0.85176 , 1.1077 ,0.0040765 ,0.0040765 ,0.0040765
+ 361 , 1.3047 , 1.2218 , 1.2752 , 0.80345 , 0.63382 , 0.73593 , 0.50578 , 1.0847 , 0.85262 , 1.1084 ,0.00406 ,0.00406 ,0.00406
+ 362 , 1.3048 , 1.2227 , 1.2758 , 0.80272 , 0.6358 , 0.73632 , 0.50596 , 1.0842 , 0.85224 , 1.1088 ,0.0040435 ,0.0040435 ,0.0040435
+ 363 , 1.3058 , 1.2246 , 1.2744 , 0.8033 , 0.63663 , 0.73645 , 0.50569 , 1.0834 , 0.8513 , 1.1089 ,0.004027 ,0.004027 ,0.004027
+ 364 , 1.3031 , 1.2216 , 1.2737 , 0.80244 , 0.63617 , 0.73667 , 0.5062 , 1.0824 , 0.85132 , 1.1084 ,0.0040105 ,0.0040105 ,0.0040105
+ 365 , 1.3045 , 1.219 , 1.2737 , 0.80268 , 0.63487 , 0.7367 , 0.5063 , 1.0818 , 0.85145 , 1.1079 ,0.003994 ,0.003994 ,0.003994
+ 366 , 1.3016 , 1.2183 , 1.2719 , 0.8047 , 0.63376 , 0.73662 , 0.50658 , 1.0809 , 0.85116 , 1.107 ,0.0039775 ,0.0039775 ,0.0039775
+ 367 , 1.3044 , 1.2216 , 1.2723 , 0.80322 , 0.63552 , 0.73702 , 0.50705 , 1.0799 , 0.85133 , 1.1061 ,0.003961 ,0.003961 ,0.003961
+ 368 , 1.3048 , 1.221 , 1.2733 , 0.80447 , 0.63412 , 0.73763 , 0.50715 , 1.0808 , 0.85125 , 1.1062 ,0.0039445 ,0.0039445 ,0.0039445
+ 369 , 1.3003 , 1.2152 , 1.2722 , 0.80791 , 0.63419 , 0.73756 , 0.50726 , 1.0814 , 0.85109 , 1.1064 ,0.003928 ,0.003928 ,0.003928
+ 370 , 1.3024 , 1.2208 , 1.272 , 0.80765 , 0.63466 , 0.73759 , 0.50724 , 1.0809 , 0.85029 , 1.1062 ,0.0039115 ,0.0039115 ,0.0039115
+ 371 , 1.3005 , 1.2169 , 1.2722 , 0.80478 , 0.63533 , 0.73776 , 0.50765 , 1.0813 , 0.85024 , 1.1062 ,0.003895 ,0.003895 ,0.003895
+ 372 , 1.3005 , 1.2157 , 1.2741 , 0.80484 , 0.63524 , 0.73752 , 0.5076 , 1.0812 , 0.85068 , 1.1071 ,0.0038785 ,0.0038785 ,0.0038785
+ 373 , 1.3 , 1.2164 , 1.2727 , 0.8094 , 0.63422 , 0.73799 , 0.50789 , 1.0812 , 0.85006 , 1.1075 ,0.003862 ,0.003862 ,0.003862
+ 374 , 1.3015 , 1.2184 , 1.2723 , 0.8103 , 0.63259 , 0.73766 , 0.50826 , 1.0803 , 0.84958 , 1.1072 ,0.0038455 ,0.0038455 ,0.0038455
+ 375 , 1.2987 , 1.2162 , 1.271 , 0.80691 , 0.63439 , 0.73825 , 0.50821 , 1.0808 , 0.85021 , 1.1073 ,0.003829 ,0.003829 ,0.003829
+ 376 , 1.303 , 1.2171 , 1.2699 , 0.80746 , 0.63459 , 0.73807 , 0.50793 , 1.0802 , 0.85032 , 1.1067 ,0.0038125 ,0.0038125 ,0.0038125
+ 377 , 1.2994 , 1.2146 , 1.2728 , 0.80663 , 0.63468 , 0.73833 , 0.5084 , 1.0794 , 0.85034 , 1.1061 ,0.003796 ,0.003796 ,0.003796
+ 378 , 1.3001 , 1.2137 , 1.2694 , 0.80646 , 0.63409 , 0.73825 , 0.50889 , 1.0781 , 0.84927 , 1.1047 ,0.0037795 ,0.0037795 ,0.0037795
+ 379 , 1.3003 , 1.2126 , 1.2706 , 0.80534 , 0.6345 , 0.73779 , 0.50889 , 1.0763 , 0.84869 , 1.1034 ,0.003763 ,0.003763 ,0.003763
+ 380 , 1.2999 , 1.2151 , 1.2694 , 0.8023 , 0.63793 , 0.73821 , 0.50878 , 1.0754 , 0.84743 , 1.1026 ,0.0037465 ,0.0037465 ,0.0037465
+ 381 , 1.2962 , 1.2108 , 1.2687 , 0.79959 , 0.63899 , 0.73808 , 0.50918 , 1.075 , 0.84697 , 1.1017 ,0.00373 ,0.00373 ,0.00373
+ 382 , 1.2983 , 1.2137 , 1.2703 , 0.80222 , 0.63869 , 0.7386 , 0.50974 , 1.075 , 0.846 , 1.1016 ,0.0037135 ,0.0037135 ,0.0037135
+ 383 , 1.2975 , 1.214 , 1.2689 , 0.80234 , 0.63812 , 0.73807 , 0.50964 , 1.0762 , 0.8456 , 1.1026 ,0.003697 ,0.003697 ,0.003697
+ 384 , 1.2976 , 1.2127 , 1.2685 , 0.8024 , 0.64072 , 0.73874 , 0.50996 , 1.076 , 0.84522 , 1.1022 ,0.0036805 ,0.0036805 ,0.0036805
+ 385 , 1.2949 , 1.2105 , 1.2666 , 0.80442 , 0.63988 , 0.7387 , 0.50987 , 1.0756 , 0.84632 , 1.1016 ,0.003664 ,0.003664 ,0.003664
+ 386 , 1.295 , 1.213 , 1.266 , 0.80486 , 0.64044 , 0.73885 , 0.51014 , 1.0757 , 0.84617 , 1.1016 ,0.0036475 ,0.0036475 ,0.0036475
+ 387 , 1.2954 , 1.2089 , 1.2671 , 0.80338 , 0.64164 , 0.74006 , 0.51017 , 1.0762 , 0.84498 , 1.1017 ,0.003631 ,0.003631 ,0.003631
+ 388 , 1.2958 , 1.2093 , 1.2664 , 0.80669 , 0.64006 , 0.74047 , 0.51013 , 1.0753 , 0.8438 , 1.1007 ,0.0036145 ,0.0036145 ,0.0036145
+ 389 , 1.2929 , 1.2102 , 1.2671 , 0.80654 , 0.64183 , 0.74034 , 0.51018 , 1.0746 , 0.84302 , 1.1006 ,0.003598 ,0.003598 ,0.003598
+ 390 , 1.2931 , 1.207 , 1.2648 , 0.80659 , 0.64007 , 0.74022 , 0.51016 , 1.0749 , 0.84223 , 1.1007 ,0.0035815 ,0.0035815 ,0.0035815
+ 391 , 1.2935 , 1.2073 , 1.264 , 0.80511 , 0.63979 , 0.74051 , 0.51048 , 1.0755 , 0.84184 , 1.1005 ,0.003565 ,0.003565 ,0.003565
+ 392 , 1.2928 , 1.2042 , 1.2632 , 0.80723 , 0.63793 , 0.74028 , 0.51057 , 1.0757 , 0.84186 , 1.1004 ,0.0035485 ,0.0035485 ,0.0035485
+ 393 , 1.2887 , 1.2051 , 1.2634 , 0.80744 , 0.63967 , 0.73991 , 0.51041 , 1.0743 , 0.84185 , 1.0997 ,0.003532 ,0.003532 ,0.003532
+ 394 , 1.2916 , 1.2049 , 1.2631 , 0.80453 , 0.64127 , 0.74058 , 0.5105 , 1.0753 , 0.84163 , 1.1003 ,0.0035155 ,0.0035155 ,0.0035155
+ 395 , 1.2932 , 1.204 , 1.2644 , 0.80557 , 0.6409 , 0.74058 , 0.51049 , 1.0753 , 0.84098 , 1.1003 ,0.003499 ,0.003499 ,0.003499
+ 396 , 1.292 , 1.2048 , 1.2644 , 0.8032 , 0.64276 , 0.74001 , 0.51033 , 1.0751 , 0.83959 , 1.1003 ,0.0034825 ,0.0034825 ,0.0034825
+ 397 , 1.291 , 1.202 , 1.2637 , 0.80171 , 0.64276 , 0.73948 , 0.51073 , 1.0749 , 0.8401 , 1.1 ,0.003466 ,0.003466 ,0.003466
+ 398 , 1.2912 , 1.2049 , 1.2613 , 0.80123 , 0.64322 , 0.74017 , 0.51083 , 1.0753 , 0.8401 , 1.0997 ,0.0034495 ,0.0034495 ,0.0034495
+ 399 , 1.2917 , 1.2026 , 1.2637 , 0.80109 , 0.64248 , 0.74028 , 0.51066 , 1.076 , 0.83949 , 1.1004 ,0.003433 ,0.003433 ,0.003433
+ 400 , 1.2904 , 1.2015 , 1.262 , 0.79985 , 0.64322 , 0.74055 , 0.51062 , 1.075 , 0.83881 , 1.0997 ,0.0034165 ,0.0034165 ,0.0034165
+ 401 , 1.2894 , 1.2009 , 1.2613 , 0.80226 , 0.64148 , 0.74032 , 0.51045 , 1.0751 , 0.83853 , 1.0999 ,0.0034 ,0.0034 ,0.0034
+ 402 , 1.2916 , 1.2041 , 1.2638 , 0.80265 , 0.64121 , 0.73963 , 0.51031 , 1.0741 , 0.83922 , 1.0996 ,0.0033835 ,0.0033835 ,0.0033835
+ 403 , 1.2898 , 1.1998 , 1.2603 , 0.80292 , 0.64202 , 0.7403 , 0.51027 , 1.0739 , 0.84024 , 1.0993 ,0.003367 ,0.003367 ,0.003367
+ 404 , 1.2859 , 1.1963 , 1.2609 , 0.80588 , 0.64061 , 0.74053 , 0.50988 , 1.0733 , 0.83969 , 1.0986 ,0.0033505 ,0.0033505 ,0.0033505
+ 405 , 1.2878 , 1.2006 , 1.2628 , 0.80483 , 0.6397 , 0.73997 , 0.51031 , 1.073 , 0.83937 , 1.0983 ,0.003334 ,0.003334 ,0.003334
+ 406 , 1.2867 , 1.2002 , 1.2593 , 0.80309 , 0.64072 , 0.74005 , 0.51057 , 1.0722 , 0.83851 , 1.0976 ,0.0033175 ,0.0033175 ,0.0033175
+ 399 , 1.2737 , 1.1785 , 1.2514 , 0.80171 , 0.64313 , 0.74007 , 0.51129 , 1.0745 , 0.83833 , 1.099 ,0.003433 ,0.003433 ,0.003433
+ 400 , 1.2817 , 1.1934 , 1.2565 , 0.80205 , 0.64294 , 0.73993 , 0.51119 , 1.0741 , 0.83827 , 1.0986 ,0.0034165 ,0.0034165 ,0.0034165
+ 401 , 1.2855 , 1.1952 , 1.2604 , 0.79875 , 0.64387 , 0.73952 , 0.5111 , 1.0729 , 0.83787 , 1.0981 ,0.0034 ,0.0034 ,0.0034
+ 402 , 1.2869 , 1.197 , 1.2604 , 0.80179 , 0.64266 , 0.73985 , 0.51131 , 1.0729 , 0.83906 , 1.0981 ,0.0033835 ,0.0033835 ,0.0033835
+ 403 , 1.2864 , 1.1989 , 1.2602 , 0.80091 , 0.64369 , 0.73933 , 0.51172 , 1.0728 , 0.84016 , 1.0981 ,0.003367 ,0.003367 ,0.003367
+ 404 , 1.2893 , 1.1986 , 1.2612 , 0.80475 , 0.64098 , 0.73901 , 0.51215 , 1.0727 , 0.83998 , 1.0975 ,0.0033505 ,0.0033505 ,0.0033505
+ 405 , 1.2879 , 1.1988 , 1.2591 , 0.80304 , 0.64278 , 0.73916 , 0.51137 , 1.0729 , 0.83993 , 1.0972 ,0.003334 ,0.003334 ,0.003334
+ 406 , 1.2835 , 1.1959 , 1.2579 , 0.80642 , 0.64089 , 0.7393 , 0.51097 , 1.0721 , 0.83988 , 1.0971 ,0.0033175 ,0.0033175 ,0.0033175
+ 407 , 1.2874 , 1.1987 , 1.2604 , 0.80939 , 0.6391 , 0.73915 , 0.51122 , 1.0709 , 0.83932 , 1.0963 ,0.003301 ,0.003301 ,0.003301
+ 408 , 1.285 , 1.1984 , 1.26 , 0.80405 , 0.64127 , 0.73932 , 0.51129 , 1.071 , 0.83863 , 1.0961 ,0.0032845 ,0.0032845 ,0.0032845
+ 409 , 1.2843 , 1.1992 , 1.2591 , 0.79981 , 0.64387 , 0.74002 , 0.51151 , 1.07 , 0.83791 , 1.0954 ,0.003268 ,0.003268 ,0.003268
+ 410 , 1.2849 , 1.1962 , 1.2607 , 0.79918 , 0.64341 , 0.73991 , 0.51119 , 1.0704 , 0.83889 , 1.0957 ,0.0032515 ,0.0032515 ,0.0032515
+ 411 , 1.2808 , 1.1952 , 1.257 , 0.79886 , 0.64146 , 0.74022 , 0.51155 , 1.0696 , 0.83892 , 1.0957 ,0.003235 ,0.003235 ,0.003235
+ 412 , 1.2842 , 1.1951 , 1.2582 , 0.80011 , 0.64248 , 0.74032 , 0.51174 , 1.0699 , 0.83904 , 1.0958 ,0.0032185 ,0.0032185 ,0.0032185
+ 413 , 1.2826 , 1.194 , 1.2587 , 0.80119 , 0.64229 , 0.74044 , 0.51171 , 1.0702 , 0.83924 , 1.0965 ,0.003202 ,0.003202 ,0.003202
+ 414 , 1.2827 , 1.193 , 1.2581 , 0.8049 , 0.64053 , 0.74075 , 0.51143 , 1.0713 , 0.83959 , 1.0969 ,0.0031855 ,0.0031855 ,0.0031855
+ 415 , 1.2826 , 1.1925 , 1.2565 , 0.80483 , 0.6398 , 0.74033 , 0.51134 , 1.0713 , 0.83905 , 1.0968 ,0.003169 ,0.003169 ,0.003169
+ 416 , 1.2838 , 1.1935 , 1.2571 , 0.80733 , 0.6396 , 0.74133 , 0.5119 , 1.0708 , 0.83909 , 1.096 ,0.0031525 ,0.0031525 ,0.0031525
+ 417 , 1.2812 , 1.1935 , 1.2542 , 0.80337 , 0.64322 , 0.74139 , 0.51179 , 1.0706 , 0.83858 , 1.0961 ,0.003136 ,0.003136 ,0.003136
+ 418 , 1.2818 , 1.1902 , 1.2546 , 0.80502 , 0.64109 , 0.74142 , 0.51187 , 1.0701 , 0.83805 , 1.0959 ,0.0031195 ,0.0031195 ,0.0031195
+ 419 , 1.2792 , 1.1912 , 1.2587 , 0.8043 , 0.64239 , 0.7412 , 0.51235 , 1.0699 , 0.83746 , 1.0962 ,0.003103 ,0.003103 ,0.003103
+ 420 , 1.2815 , 1.1899 , 1.256 , 0.80462 , 0.64304 , 0.74191 , 0.51232 , 1.0697 , 0.83752 , 1.0964 ,0.0030865 ,0.0030865 ,0.0030865
+ 421 , 1.283 , 1.1927 , 1.2579 , 0.80533 , 0.64334 , 0.74158 , 0.51224 , 1.0696 , 0.83757 , 1.0966 ,0.00307 ,0.00307 ,0.00307
+ 422 , 1.2763 , 1.1868 , 1.2541 , 0.80635 , 0.64341 , 0.74214 , 0.51263 , 1.0699 , 0.83737 , 1.0969 ,0.0030535 ,0.0030535 ,0.0030535
+ 423 , 1.2797 , 1.1886 , 1.2553 , 0.81031 , 0.64174 , 0.74283 , 0.51302 , 1.0694 , 0.83751 , 1.0968 ,0.003037 ,0.003037 ,0.003037
+ 424 , 1.2759 , 1.1816 , 1.2532 , 0.80689 , 0.64053 , 0.74207 , 0.51318 , 1.0674 , 0.8366 , 1.0958 ,0.0030205 ,0.0030205 ,0.0030205
+ 425 , 1.2779 , 1.1865 , 1.2527 , 0.8046 , 0.64164 , 0.74207 , 0.51377 , 1.0663 , 0.83655 , 1.0955 ,0.003004 ,0.003004 ,0.003004
+ 426 , 1.2746 , 1.1809 , 1.2556 , 0.80387 , 0.64196 , 0.74196 , 0.5135 , 1.0655 , 0.83587 , 1.0954 ,0.0029875 ,0.0029875 ,0.0029875
+ 427 , 1.2765 , 1.1826 , 1.2535 , 0.8096 , 0.63997 , 0.74201 , 0.51381 , 1.0652 , 0.83568 , 1.0957 ,0.002971 ,0.002971 ,0.002971
+ 428 , 1.2781 , 1.1858 , 1.2537 , 0.80693 , 0.64072 , 0.74203 , 0.51371 , 1.0649 , 0.83516 , 1.0953 ,0.0029545 ,0.0029545 ,0.0029545
+ 429 , 1.2759 , 1.1876 , 1.2542 , 0.80901 , 0.64062 , 0.74309 , 0.51389 , 1.0652 , 0.83484 , 1.0957 ,0.002938 ,0.002938 ,0.002938
+ 430 , 1.2755 , 1.1838 , 1.2535 , 0.80592 , 0.6423 , 0.74315 , 0.51408 , 1.0661 , 0.83361 , 1.0963 ,0.0029215 ,0.0029215 ,0.0029215
+ 431 , 1.2776 , 1.1835 , 1.2524 , 0.80924 , 0.64321 , 0.74302 , 0.5137 , 1.0657 , 0.83326 , 1.0953 ,0.002905 ,0.002905 ,0.002905
+ 432 , 1.2739 , 1.1801 , 1.2523 , 0.80711 , 0.6449 , 0.7429 , 0.51374 , 1.066 , 0.83293 , 1.0946 ,0.0028885 ,0.0028885 ,0.0028885
+ 433 , 1.2766 , 1.1827 , 1.253 , 0.80508 , 0.64464 , 0.74287 , 0.51351 , 1.0663 , 0.83267 , 1.0946 ,0.002872 ,0.002872 ,0.002872
+ 434 , 1.2747 , 1.1838 , 1.2519 , 0.80649 , 0.64517 , 0.74336 , 0.51352 , 1.0662 , 0.83303 , 1.0946 ,0.0028555 ,0.0028555 ,0.0028555
+ 435 , 1.2734 , 1.1834 , 1.2508 , 0.80482 , 0.64721 , 0.74361 , 0.51369 , 1.0657 , 0.83377 , 1.094 ,0.002839 ,0.002839 ,0.002839
+ 436 , 1.2726 , 1.1786 , 1.2484 , 0.80697 , 0.64509 , 0.74343 , 0.5135 , 1.0652 , 0.83293 , 1.0935 ,0.0028225 ,0.0028225 ,0.0028225
+ 437 , 1.2754 , 1.1812 , 1.2495 , 0.80933 , 0.64396 , 0.74314 , 0.51425 , 1.0644 , 0.83233 , 1.093 ,0.002806 ,0.002806 ,0.002806
+ 438 , 1.2735 , 1.1803 , 1.2501 , 0.80647 , 0.64378 , 0.74325 , 0.51449 , 1.0647 , 0.83198 , 1.0936 ,0.0027895 ,0.0027895 ,0.0027895
+ 439 , 1.2694 , 1.1762 , 1.2491 , 0.80908 , 0.64253 , 0.7439 , 0.51505 , 1.0643 , 0.83122 , 1.0937 ,0.002773 ,0.002773 ,0.002773
+ 440 , 1.2706 , 1.1818 , 1.2498 , 0.81205 , 0.64081 , 0.74383 , 0.51526 , 1.0643 , 0.83146 , 1.0941 ,0.0027565 ,0.0027565 ,0.0027565
+ 441 , 1.2674 , 1.1763 , 1.2477 , 0.81074 , 0.63979 , 0.74382 , 0.51541 , 1.0634 , 0.83231 , 1.0935 ,0.00274 ,0.00274 ,0.00274
+ 442 , 1.2687 , 1.1734 , 1.2474 , 0.80846 , 0.63988 , 0.74373 , 0.51535 , 1.0631 , 0.83177 , 1.0936 ,0.0027235 ,0.0027235 ,0.0027235
+ 443 , 1.2709 , 1.1762 , 1.2499 , 0.81312 , 0.63923 , 0.74381 , 0.51555 , 1.0634 , 0.83169 , 1.0938 ,0.002707 ,0.002707 ,0.002707
+ 444 , 1.2704 , 1.1731 , 1.2482 , 0.8111 , 0.63946 , 0.74341 , 0.51535 , 1.0629 , 0.83143 , 1.0931 ,0.0026905 ,0.0026905 ,0.0026905
+ 445 , 1.2701 , 1.1754 , 1.248 , 0.81151 , 0.64016 , 0.7438 , 0.51561 , 1.0629 , 0.8308 , 1.0927 ,0.002674 ,0.002674 ,0.002674
+ 446 , 1.2671 , 1.1726 , 1.2475 , 0.81315 , 0.63932 , 0.74442 , 0.51612 , 1.0626 , 0.83015 , 1.0925 ,0.0026575 ,0.0026575 ,0.0026575
+ 447 , 1.268 , 1.1709 , 1.2456 , 0.81296 , 0.63963 , 0.74414 , 0.516 , 1.0625 , 0.82986 , 1.0923 ,0.002641 ,0.002641 ,0.002641
+ 448 , 1.2665 , 1.1734 , 1.2464 , 0.81181 , 0.63932 , 0.74432 , 0.51617 , 1.0627 , 0.8303 , 1.0925 ,0.0026245 ,0.0026245 ,0.0026245
+ 449 , 1.2674 , 1.1716 , 1.2468 , 0.81125 , 0.64137 , 0.74456 , 0.51605 , 1.0623 , 0.83069 , 1.092 ,0.002608 ,0.002608 ,0.002608
+ 450 , 1.2646 , 1.1706 , 1.2436 , 0.81074 , 0.64248 , 0.74462 , 0.51646 , 1.0623 , 0.83049 , 1.0914 ,0.0025915 ,0.0025915 ,0.0025915
+ 451 , 1.2646 , 1.1681 , 1.2454 , 0.80818 , 0.64489 , 0.74439 , 0.51666 , 1.0616 , 0.83012 , 1.0906 ,0.002575 ,0.002575 ,0.002575
+ 452 , 1.264 , 1.1646 , 1.2434 , 0.80707 , 0.64318 , 0.74498 , 0.51643 , 1.0616 , 0.83012 , 1.0908 ,0.0025585 ,0.0025585 ,0.0025585
+ 453 , 1.2625 , 1.1645 , 1.2422 , 0.80526 , 0.64331 , 0.74439 , 0.51638 , 1.0609 , 0.82902 , 1.0903 ,0.002542 ,0.002542 ,0.002542
+ 454 , 1.2628 , 1.163 , 1.2448 , 0.80661 , 0.64243 , 0.74421 , 0.51663 , 1.0603 , 0.82876 , 1.0897 ,0.0025255 ,0.0025255 ,0.0025255
+ 455 , 1.261 , 1.1651 , 1.242 , 0.80678 , 0.64316 , 0.74454 , 0.51719 , 1.0603 , 0.8285 , 1.0892 ,0.002509 ,0.002509 ,0.002509
+ 456 , 1.2638 , 1.1657 , 1.2437 , 0.80905 , 0.64164 , 0.74486 , 0.51693 , 1.0604 , 0.82752 , 1.0886 ,0.0024925 ,0.0024925 ,0.0024925
+ 457 , 1.2625 , 1.1675 , 1.2426 , 0.8115 , 0.63942 , 0.74519 , 0.51715 , 1.0605 , 0.82778 , 1.0882 ,0.002476 ,0.002476 ,0.002476
+ 458 , 1.2639 , 1.1637 , 1.2438 , 0.80746 , 0.64257 , 0.74486 , 0.51702 , 1.06 , 0.82809 , 1.0881 ,0.0024595 ,0.0024595 ,0.0024595
+ 459 , 1.26 , 1.1629 , 1.2414 , 0.80627 , 0.64337 , 0.74564 , 0.51755 , 1.06 , 0.82636 , 1.0879 ,0.002443 ,0.002443 ,0.002443
+ 460 , 1.2607 , 1.1612 , 1.2413 , 0.80742 , 0.64113 , 0.74478 , 0.51724 , 1.0597 , 0.82648 , 1.0875 ,0.0024265 ,0.0024265 ,0.0024265
+ 461 , 1.2625 , 1.1629 , 1.2412 , 0.80944 , 0.64048 , 0.74508 , 0.51751 , 1.059 , 0.82584 , 1.0871 ,0.00241 ,0.00241 ,0.00241
+ 462 , 1.2562 , 1.1612 , 1.241 , 0.81119 , 0.63951 , 0.74554 , 0.51733 , 1.0589 , 0.82623 , 1.087 ,0.0023935 ,0.0023935 ,0.0023935
+ 463 , 1.2583 , 1.1637 , 1.2415 , 0.80953 , 0.64047 , 0.74517 , 0.51695 , 1.0583 , 0.8254 , 1.0869 ,0.002377 ,0.002377 ,0.002377
+ 464 , 1.2607 , 1.1623 , 1.2404 , 0.81669 , 0.63775 , 0.74633 , 0.5173 , 1.0577 , 0.82503 , 1.0858 ,0.0023605 ,0.0023605 ,0.0023605
+ 465 , 1.258 , 1.1575 , 1.2401 , 0.81063 , 0.64186 , 0.74569 , 0.51768 , 1.0568 , 0.82521 , 1.0853 ,0.002344 ,0.002344 ,0.002344
+ 466 , 1.2557 , 1.1571 , 1.2398 , 0.81139 , 0.64072 , 0.74606 , 0.51768 , 1.0569 , 0.82484 , 1.0855 ,0.0023275 ,0.0023275 ,0.0023275
+ 467 , 1.2577 , 1.1569 , 1.2392 , 0.80944 , 0.64192 , 0.74663 , 0.51764 , 1.0573 , 0.82478 , 1.0863 ,0.002311 ,0.002311 ,0.002311
+ 468 , 1.2513 , 1.1555 , 1.2367 , 0.80954 , 0.64327 , 0.74649 , 0.51795 , 1.0563 , 0.82407 , 1.0857 ,0.0022945 ,0.0022945 ,0.0022945
+ 469 , 1.2566 , 1.155 , 1.2389 , 0.81258 , 0.64257 , 0.7473 , 0.51849 , 1.0566 , 0.82372 , 1.0851 ,0.002278 ,0.002278 ,0.002278
+ 470 , 1.2575 , 1.1616 , 1.2394 , 0.81297 , 0.64212 , 0.74725 , 0.51858 , 1.0565 , 0.82369 , 1.0851 ,0.0022615 ,0.0022615 ,0.0022615
+ 471 , 1.2559 , 1.1576 , 1.2393 , 0.80815 , 0.64601 , 0.74743 , 0.51917 , 1.0563 , 0.82285 , 1.0854 ,0.002245 ,0.002245 ,0.002245
+ 472 , 1.2522 , 1.1516 , 1.236 , 0.80709 , 0.64387 , 0.74665 , 0.51881 , 1.0561 , 0.82335 , 1.0854 ,0.0022285 ,0.0022285 ,0.0022285
+ 473 , 1.2539 , 1.154 , 1.2366 , 0.80846 , 0.64276 , 0.74667 , 0.51899 , 1.0554 , 0.82371 , 1.0848 ,0.002212 ,0.002212 ,0.002212
+ 474 , 1.2508 , 1.1483 , 1.2359 , 0.81029 , 0.64324 , 0.74695 , 0.51886 , 1.0565 , 0.82402 , 1.0852 ,0.0021955 ,0.0021955 ,0.0021955
+ 475 , 1.2497 , 1.1518 , 1.2371 , 0.81098 , 0.64174 , 0.74676 , 0.519 , 1.056 , 0.82452 , 1.0846 ,0.002179 ,0.002179 ,0.002179
+ 476 , 1.2528 , 1.1535 , 1.2382 , 0.81343 , 0.64155 , 0.74731 , 0.5192 , 1.0553 , 0.82388 , 1.0843 ,0.0021625 ,0.0021625 ,0.0021625
+ 477 , 1.2561 , 1.1513 , 1.2354 , 0.8108 , 0.64341 , 0.7476 , 0.51934 , 1.0552 , 0.82445 , 1.0845 ,0.002146 ,0.002146 ,0.002146
+ 399 , 1.2737 , 1.1785 , 1.2514 , 0.80171 , 0.64313 , 0.74007 , 0.51129 , 1.0745 , 0.83833 , 1.099 ,0.003433 ,0.003433 ,0.003433
+ 400 , 1.2817 , 1.1934 , 1.2565 , 0.80205 , 0.64294 , 0.73993 , 0.51119 , 1.0741 , 0.83827 , 1.0986 ,0.0034165 ,0.0034165 ,0.0034165
+ 401 , 1.2855 , 1.1952 , 1.2604 , 0.79875 , 0.64387 , 0.73952 , 0.5111 , 1.0729 , 0.83787 , 1.0981 ,0.0034 ,0.0034 ,0.0034
+ 402 , 1.2869 , 1.197 , 1.2604 , 0.80179 , 0.64266 , 0.73985 , 0.51131 , 1.0729 , 0.83906 , 1.0981 ,0.0033835 ,0.0033835 ,0.0033835
+ 403 , 1.2864 , 1.1989 , 1.2602 , 0.80091 , 0.64369 , 0.73933 , 0.51172 , 1.0728 , 0.84016 , 1.0981 ,0.003367 ,0.003367 ,0.003367
+ 404 , 1.2893 , 1.1986 , 1.2612 , 0.80475 , 0.64098 , 0.73901 , 0.51215 , 1.0727 , 0.83998 , 1.0975 ,0.0033505 ,0.0033505 ,0.0033505
+ 405 , 1.2879 , 1.1988 , 1.2591 , 0.80304 , 0.64278 , 0.73916 , 0.51137 , 1.0729 , 0.83993 , 1.0972 ,0.003334 ,0.003334 ,0.003334
+ 406 , 1.2835 , 1.1959 , 1.2579 , 0.80642 , 0.64089 , 0.7393 , 0.51097 , 1.0721 , 0.83988 , 1.0971 ,0.0033175 ,0.0033175 ,0.0033175
+ 407 , 1.2874 , 1.1987 , 1.2604 , 0.80939 , 0.6391 , 0.73915 , 0.51122 , 1.0709 , 0.83932 , 1.0963 ,0.003301 ,0.003301 ,0.003301
+ 408 , 1.285 , 1.1984 , 1.26 , 0.80405 , 0.64127 , 0.73932 , 0.51129 , 1.071 , 0.83863 , 1.0961 ,0.0032845 ,0.0032845 ,0.0032845
+ 409 , 1.2843 , 1.1992 , 1.2591 , 0.79981 , 0.64387 , 0.74002 , 0.51151 , 1.07 , 0.83791 , 1.0954 ,0.003268 ,0.003268 ,0.003268
+ 410 , 1.2849 , 1.1962 , 1.2607 , 0.79918 , 0.64341 , 0.73991 , 0.51119 , 1.0704 , 0.83889 , 1.0957 ,0.0032515 ,0.0032515 ,0.0032515
+ 411 , 1.2808 , 1.1952 , 1.257 , 0.79886 , 0.64146 , 0.74022 , 0.51155 , 1.0696 , 0.83892 , 1.0957 ,0.003235 ,0.003235 ,0.003235
+ 412 , 1.2842 , 1.1951 , 1.2582 , 0.80011 , 0.64248 , 0.74032 , 0.51174 , 1.0699 , 0.83904 , 1.0958 ,0.0032185 ,0.0032185 ,0.0032185
+ 413 , 1.2826 , 1.194 , 1.2587 , 0.80119 , 0.64229 , 0.74044 , 0.51171 , 1.0702 , 0.83924 , 1.0965 ,0.003202 ,0.003202 ,0.003202
+ 414 , 1.2827 , 1.193 , 1.2581 , 0.8049 , 0.64053 , 0.74075 , 0.51143 , 1.0713 , 0.83959 , 1.0969 ,0.0031855 ,0.0031855 ,0.0031855
+ 415 , 1.2826 , 1.1925 , 1.2565 , 0.80483 , 0.6398 , 0.74033 , 0.51134 , 1.0713 , 0.83905 , 1.0968 ,0.003169 ,0.003169 ,0.003169
+ 416 , 1.2838 , 1.1935 , 1.2571 , 0.80733 , 0.6396 , 0.74133 , 0.5119 , 1.0708 , 0.83909 , 1.096 ,0.0031525 ,0.0031525 ,0.0031525
+ 417 , 1.2812 , 1.1935 , 1.2542 , 0.80337 , 0.64322 , 0.74139 , 0.51179 , 1.0706 , 0.83858 , 1.0961 ,0.003136 ,0.003136 ,0.003136
+ 418 , 1.2818 , 1.1902 , 1.2546 , 0.80502 , 0.64109 , 0.74142 , 0.51187 , 1.0701 , 0.83805 , 1.0959 ,0.0031195 ,0.0031195 ,0.0031195
+ 419 , 1.2792 , 1.1912 , 1.2587 , 0.8043 , 0.64239 , 0.7412 , 0.51235 , 1.0699 , 0.83746 , 1.0962 ,0.003103 ,0.003103 ,0.003103
+ 420 , 1.2815 , 1.1899 , 1.256 , 0.80462 , 0.64304 , 0.74191 , 0.51232 , 1.0697 , 0.83752 , 1.0964 ,0.0030865 ,0.0030865 ,0.0030865
+ 421 , 1.283 , 1.1927 , 1.2579 , 0.80533 , 0.64334 , 0.74158 , 0.51224 , 1.0696 , 0.83757 , 1.0966 ,0.00307 ,0.00307 ,0.00307
+ 422 , 1.2763 , 1.1868 , 1.2541 , 0.80635 , 0.64341 , 0.74214 , 0.51263 , 1.0699 , 0.83737 , 1.0969 ,0.0030535 ,0.0030535 ,0.0030535
+ 423 , 1.2797 , 1.1886 , 1.2553 , 0.81031 , 0.64174 , 0.74283 , 0.51302 , 1.0694 , 0.83751 , 1.0968 ,0.003037 ,0.003037 ,0.003037
+ 424 , 1.2759 , 1.1816 , 1.2532 , 0.80689 , 0.64053 , 0.74207 , 0.51318 , 1.0674 , 0.8366 , 1.0958 ,0.0030205 ,0.0030205 ,0.0030205
+ 425 , 1.2779 , 1.1865 , 1.2527 , 0.8046 , 0.64164 , 0.74207 , 0.51377 , 1.0663 , 0.83655 , 1.0955 ,0.003004 ,0.003004 ,0.003004
+ 426 , 1.2746 , 1.1809 , 1.2556 , 0.80387 , 0.64196 , 0.74196 , 0.5135 , 1.0655 , 0.83587 , 1.0954 ,0.0029875 ,0.0029875 ,0.0029875
+ 427 , 1.2765 , 1.1826 , 1.2535 , 0.8096 , 0.63997 , 0.74201 , 0.51381 , 1.0652 , 0.83568 , 1.0957 ,0.002971 ,0.002971 ,0.002971
+ 428 , 1.2781 , 1.1858 , 1.2537 , 0.80693 , 0.64072 , 0.74203 , 0.51371 , 1.0649 , 0.83516 , 1.0953 ,0.0029545 ,0.0029545 ,0.0029545
+ 429 , 1.2759 , 1.1876 , 1.2542 , 0.80901 , 0.64062 , 0.74309 , 0.51389 , 1.0652 , 0.83484 , 1.0957 ,0.002938 ,0.002938 ,0.002938
+ 430 , 1.2755 , 1.1838 , 1.2535 , 0.80592 , 0.6423 , 0.74315 , 0.51408 , 1.0661 , 0.83361 , 1.0963 ,0.0029215 ,0.0029215 ,0.0029215
+ 431 , 1.2776 , 1.1835 , 1.2524 , 0.80924 , 0.64321 , 0.74302 , 0.5137 , 1.0657 , 0.83326 , 1.0953 ,0.002905 ,0.002905 ,0.002905
+ 432 , 1.2739 , 1.1801 , 1.2523 , 0.80711 , 0.6449 , 0.7429 , 0.51374 , 1.066 , 0.83293 , 1.0946 ,0.0028885 ,0.0028885 ,0.0028885
+ 433 , 1.2766 , 1.1827 , 1.253 , 0.80508 , 0.64464 , 0.74287 , 0.51351 , 1.0663 , 0.83267 , 1.0946 ,0.002872 ,0.002872 ,0.002872
+ 434 , 1.2747 , 1.1838 , 1.2519 , 0.80649 , 0.64517 , 0.74336 , 0.51352 , 1.0662 , 0.83303 , 1.0946 ,0.0028555 ,0.0028555 ,0.0028555
+ 435 , 1.2734 , 1.1834 , 1.2508 , 0.80482 , 0.64721 , 0.74361 , 0.51369 , 1.0657 , 0.83377 , 1.094 ,0.002839 ,0.002839 ,0.002839
+ 436 , 1.2726 , 1.1786 , 1.2484 , 0.80697 , 0.64509 , 0.74343 , 0.5135 , 1.0652 , 0.83293 , 1.0935 ,0.0028225 ,0.0028225 ,0.0028225
+ 437 , 1.2754 , 1.1812 , 1.2495 , 0.80933 , 0.64396 , 0.74314 , 0.51425 , 1.0644 , 0.83233 , 1.093 ,0.002806 ,0.002806 ,0.002806
+ 438 , 1.2735 , 1.1803 , 1.2501 , 0.80647 , 0.64378 , 0.74325 , 0.51449 , 1.0647 , 0.83198 , 1.0936 ,0.0027895 ,0.0027895 ,0.0027895
+ 439 , 1.2694 , 1.1762 , 1.2491 , 0.80908 , 0.64253 , 0.7439 , 0.51505 , 1.0643 , 0.83122 , 1.0937 ,0.002773 ,0.002773 ,0.002773
+ 440 , 1.2706 , 1.1818 , 1.2498 , 0.81205 , 0.64081 , 0.74383 , 0.51526 , 1.0643 , 0.83146 , 1.0941 ,0.0027565 ,0.0027565 ,0.0027565
+ 441 , 1.2674 , 1.1763 , 1.2477 , 0.81074 , 0.63979 , 0.74382 , 0.51541 , 1.0634 , 0.83231 , 1.0935 ,0.00274 ,0.00274 ,0.00274
+ 442 , 1.2687 , 1.1734 , 1.2474 , 0.80846 , 0.63988 , 0.74373 , 0.51535 , 1.0631 , 0.83177 , 1.0936 ,0.0027235 ,0.0027235 ,0.0027235
+ 443 , 1.2709 , 1.1762 , 1.2499 , 0.81312 , 0.63923 , 0.74381 , 0.51555 , 1.0634 , 0.83169 , 1.0938 ,0.002707 ,0.002707 ,0.002707
+ 444 , 1.2704 , 1.1731 , 1.2482 , 0.8111 , 0.63946 , 0.74341 , 0.51535 , 1.0629 , 0.83143 , 1.0931 ,0.0026905 ,0.0026905 ,0.0026905
+ 445 , 1.2701 , 1.1754 , 1.248 , 0.81151 , 0.64016 , 0.7438 , 0.51561 , 1.0629 , 0.8308 , 1.0927 ,0.002674 ,0.002674 ,0.002674
+ 446 , 1.2671 , 1.1726 , 1.2475 , 0.81315 , 0.63932 , 0.74442 , 0.51612 , 1.0626 , 0.83015 , 1.0925 ,0.0026575 ,0.0026575 ,0.0026575
+ 447 , 1.268 , 1.1709 , 1.2456 , 0.81296 , 0.63963 , 0.74414 , 0.516 , 1.0625 , 0.82986 , 1.0923 ,0.002641 ,0.002641 ,0.002641
+ 448 , 1.2665 , 1.1734 , 1.2464 , 0.81181 , 0.63932 , 0.74432 , 0.51617 , 1.0627 , 0.8303 , 1.0925 ,0.0026245 ,0.0026245 ,0.0026245
+ 449 , 1.2674 , 1.1716 , 1.2468 , 0.81125 , 0.64137 , 0.74456 , 0.51605 , 1.0623 , 0.83069 , 1.092 ,0.002608 ,0.002608 ,0.002608
+ 450 , 1.2646 , 1.1706 , 1.2436 , 0.81074 , 0.64248 , 0.74462 , 0.51646 , 1.0623 , 0.83049 , 1.0914 ,0.0025915 ,0.0025915 ,0.0025915
+ 451 , 1.2646 , 1.1681 , 1.2454 , 0.80818 , 0.64489 , 0.74439 , 0.51666 , 1.0616 , 0.83012 , 1.0906 ,0.002575 ,0.002575 ,0.002575
+ 452 , 1.264 , 1.1646 , 1.2434 , 0.80707 , 0.64318 , 0.74498 , 0.51643 , 1.0616 , 0.83012 , 1.0908 ,0.0025585 ,0.0025585 ,0.0025585
+ 453 , 1.2625 , 1.1645 , 1.2422 , 0.80526 , 0.64331 , 0.74439 , 0.51638 , 1.0609 , 0.82902 , 1.0903 ,0.002542 ,0.002542 ,0.002542
+ 454 , 1.2628 , 1.163 , 1.2448 , 0.80661 , 0.64243 , 0.74421 , 0.51663 , 1.0603 , 0.82876 , 1.0897 ,0.0025255 ,0.0025255 ,0.0025255
+ 455 , 1.261 , 1.1651 , 1.242 , 0.80678 , 0.64316 , 0.74454 , 0.51719 , 1.0603 , 0.8285 , 1.0892 ,0.002509 ,0.002509 ,0.002509
+ 456 , 1.2638 , 1.1657 , 1.2437 , 0.80905 , 0.64164 , 0.74486 , 0.51693 , 1.0604 , 0.82752 , 1.0886 ,0.0024925 ,0.0024925 ,0.0024925
+ 457 , 1.2625 , 1.1675 , 1.2426 , 0.8115 , 0.63942 , 0.74519 , 0.51715 , 1.0605 , 0.82778 , 1.0882 ,0.002476 ,0.002476 ,0.002476
+ 458 , 1.2639 , 1.1637 , 1.2438 , 0.80746 , 0.64257 , 0.74486 , 0.51702 , 1.06 , 0.82809 , 1.0881 ,0.0024595 ,0.0024595 ,0.0024595
+ 459 , 1.26 , 1.1629 , 1.2414 , 0.80627 , 0.64337 , 0.74564 , 0.51755 , 1.06 , 0.82636 , 1.0879 ,0.002443 ,0.002443 ,0.002443
+ 460 , 1.2607 , 1.1612 , 1.2413 , 0.80742 , 0.64113 , 0.74478 , 0.51724 , 1.0597 , 0.82648 , 1.0875 ,0.0024265 ,0.0024265 ,0.0024265
+ 461 , 1.2625 , 1.1629 , 1.2412 , 0.80944 , 0.64048 , 0.74508 , 0.51751 , 1.059 , 0.82584 , 1.0871 ,0.00241 ,0.00241 ,0.00241
+ 462 , 1.2562 , 1.1612 , 1.241 , 0.81119 , 0.63951 , 0.74554 , 0.51733 , 1.0589 , 0.82623 , 1.087 ,0.0023935 ,0.0023935 ,0.0023935
+ 463 , 1.2583 , 1.1637 , 1.2415 , 0.80953 , 0.64047 , 0.74517 , 0.51695 , 1.0583 , 0.8254 , 1.0869 ,0.002377 ,0.002377 ,0.002377
+ 464 , 1.2607 , 1.1623 , 1.2404 , 0.81669 , 0.63775 , 0.74633 , 0.5173 , 1.0577 , 0.82503 , 1.0858 ,0.0023605 ,0.0023605 ,0.0023605
+ 465 , 1.258 , 1.1575 , 1.2401 , 0.81063 , 0.64186 , 0.74569 , 0.51768 , 1.0568 , 0.82521 , 1.0853 ,0.002344 ,0.002344 ,0.002344
+ 466 , 1.2557 , 1.1571 , 1.2398 , 0.81139 , 0.64072 , 0.74606 , 0.51768 , 1.0569 , 0.82484 , 1.0855 ,0.0023275 ,0.0023275 ,0.0023275
+ 467 , 1.2577 , 1.1569 , 1.2392 , 0.80944 , 0.64192 , 0.74663 , 0.51764 , 1.0573 , 0.82478 , 1.0863 ,0.002311 ,0.002311 ,0.002311
+ 468 , 1.2513 , 1.1555 , 1.2367 , 0.80954 , 0.64327 , 0.74649 , 0.51795 , 1.0563 , 0.82407 , 1.0857 ,0.0022945 ,0.0022945 ,0.0022945
+ 469 , 1.2566 , 1.155 , 1.2389 , 0.81258 , 0.64257 , 0.7473 , 0.51849 , 1.0566 , 0.82372 , 1.0851 ,0.002278 ,0.002278 ,0.002278
+ 470 , 1.2575 , 1.1616 , 1.2394 , 0.81297 , 0.64212 , 0.74725 , 0.51858 , 1.0565 , 0.82369 , 1.0851 ,0.0022615 ,0.0022615 ,0.0022615
+ 471 , 1.2559 , 1.1576 , 1.2393 , 0.80815 , 0.64601 , 0.74743 , 0.51917 , 1.0563 , 0.82285 , 1.0854 ,0.002245 ,0.002245 ,0.002245
+ 472 , 1.2522 , 1.1516 , 1.236 , 0.80709 , 0.64387 , 0.74665 , 0.51881 , 1.0561 , 0.82335 , 1.0854 ,0.0022285 ,0.0022285 ,0.0022285
+ 473 , 1.2539 , 1.154 , 1.2366 , 0.80846 , 0.64276 , 0.74667 , 0.51899 , 1.0554 , 0.82371 , 1.0848 ,0.002212 ,0.002212 ,0.002212
+ 474 , 1.2508 , 1.1483 , 1.2359 , 0.81029 , 0.64324 , 0.74695 , 0.51886 , 1.0565 , 0.82402 , 1.0852 ,0.0021955 ,0.0021955 ,0.0021955
+ 475 , 1.2497 , 1.1518 , 1.2371 , 0.81098 , 0.64174 , 0.74676 , 0.519 , 1.056 , 0.82452 , 1.0846 ,0.002179 ,0.002179 ,0.002179
+ 476 , 1.2528 , 1.1535 , 1.2382 , 0.81343 , 0.64155 , 0.74731 , 0.5192 , 1.0553 , 0.82388 , 1.0843 ,0.0021625 ,0.0021625 ,0.0021625
+ 477 , 1.2561 , 1.1513 , 1.2354 , 0.8108 , 0.64341 , 0.7476 , 0.51934 , 1.0552 , 0.82445 , 1.0845 ,0.002146 ,0.002146 ,0.002146
+ 478 , 1.251 , 1.1494 , 1.2341 , 0.8088 , 0.64569 , 0.74754 , 0.51947 , 1.0558 , 0.82387 , 1.0849 ,0.0021295 ,0.0021295 ,0.0021295
+ 479 , 1.2495 , 1.1499 , 1.2361 , 0.81341 , 0.64341 , 0.74806 , 0.5194 , 1.0553 , 0.82351 , 1.0844 ,0.002113 ,0.002113 ,0.002113
+ 480 , 1.2493 , 1.1487 , 1.2347 , 0.8099 , 0.64619 , 0.74748 , 0.51941 , 1.0553 , 0.82314 , 1.0843 ,0.0020965 ,0.0020965 ,0.0020965
+ 481 , 1.2521 , 1.1475 , 1.2351 , 0.80789 , 0.64684 , 0.74741 , 0.51951 , 1.0552 , 0.82273 , 1.0842 ,0.00208 ,0.00208 ,0.00208
+ 482 , 1.2475 , 1.1442 , 1.2323 , 0.80814 , 0.64675 , 0.74747 , 0.5201 , 1.0549 , 0.82242 , 1.0844 ,0.0020635 ,0.0020635 ,0.0020635
+ 483 , 1.2509 , 1.1472 , 1.2314 , 0.80896 , 0.64563 , 0.74733 , 0.51927 , 1.0542 , 0.8217 , 1.0838 ,0.002047 ,0.002047 ,0.002047
+ 484 , 1.246 , 1.1439 , 1.2308 , 0.80972 , 0.6452 , 0.74767 , 0.51954 , 1.054 , 0.82151 , 1.084 ,0.0020305 ,0.0020305 ,0.0020305
+ 483 , 1.2321 , 1.124 , 1.2228 , 0.80724 , 0.64659 , 0.74758 , 0.51991 , 1.0538 , 0.82124 , 1.0837 ,0.002047 ,0.002047 ,0.002047
+ 484 , 1.2389 , 1.1357 , 1.2279 , 0.80754 , 0.64684 , 0.7483 , 0.52001 , 1.053 , 0.82094 , 1.0833 ,0.0020305 ,0.0020305 ,0.0020305
+ 485 , 1.2421 , 1.1362 , 1.2298 , 0.80502 , 0.64782 , 0.74841 , 0.52026 , 1.0526 , 0.82017 , 1.0829 ,0.002014 ,0.002014 ,0.002014
+
\ No newline at end of file
diff --git a/following/models/yolov8n_coco(imgsz480x640_FP16_485epoch).engine b/following/models/yolov8n_coco(imgsz480x640_FP16_485epoch).engine
new file mode 100644
index 00000000..23c56f23
Binary files /dev/null and b/following/models/yolov8n_coco(imgsz480x640_FP16_485epoch).engine differ
diff --git a/inference/.DS_Store b/inference/.DS_Store
new file mode 100644
index 00000000..fa535432
Binary files /dev/null and b/inference/.DS_Store differ
diff --git a/pilot/pilot/relay.py b/pilot/pilot/relay.py
index 60b063dd..c6f70fdb 100644
--- a/pilot/pilot/relay.py
+++ b/pilot/pilot/relay.py
@@ -100,13 +100,13 @@ def _send_config(self, data):
if self._pi_client is not None and data is not None:
self._pi_client.call(dict(time=timestamp(), method="ras/driver/config", data=data))
- def _send_drive(self, throttle=0.0, steering=0.0, reverse_gear=False, wakeup=False):
+ def _send_drive(self, throttle=0.0, steering=0.0, reverse_gear=False, wakeup=False, spin=None):
if self._pi_client is not None:
throttle = max(-1.0, min(1.0, throttle))
steering = max(-1.0, min(1.0, steering))
_reverse = 1 if reverse_gear else 0
_wakeup = 1 if wakeup else 0
- self._pi_client.call(dict(time=timestamp(), method="ras/servo/drive", data=dict(steering=steering, throttle=throttle, reverse=_reverse, wakeup=_wakeup)))
+ self._pi_client.call(dict(time=timestamp(), method="ras/servo/drive", data=dict(steering=steering, throttle=throttle, reverse=_reverse, wakeup=_wakeup, spin=spin)))
def _drive(self, pilot, teleop):
pi_status = None if self._pi_status is None else self._pi_status.pop_latest()
@@ -117,7 +117,7 @@ def _drive(self, pilot, teleop):
else:
_reverse = teleop and teleop.get("arrow_down", 0)
_wakeup = teleop and teleop.get("button_b", 0)
- self._send_drive(steering=pilot.get("steering"), throttle=pilot.get("throttle"), reverse_gear=_reverse, wakeup=_wakeup)
+ self._send_drive(steering=pilot.get("steering"), throttle=pilot.get("throttle"), reverse_gear=_reverse, wakeup=_wakeup, spin=pilot.get("spin"))
def _config(self):
parser = SafeConfigParser()
diff --git a/raspi/.DS_Store b/raspi/.DS_Store
new file mode 100644
index 00000000..5dd0981a
Binary files /dev/null and b/raspi/.DS_Store differ
diff --git a/raspi/docker-compose.yml b/raspi/docker-compose.yml
index e5b4f1b3..a1ada11f 100644
--- a/raspi/docker-compose.yml
+++ b/raspi/docker-compose.yml
@@ -9,7 +9,7 @@ services:
privileged: true
user: root
restart: always
- network_mode: "host"
+ network_mode: 'host'
command: bash -c "/bin/rm -rf /var/run/pigpio.pid && /pigpio/pigpiod -gl"
zerotier:
image: zyclonite/zerotier:1.6.6
@@ -35,14 +35,14 @@ services:
user: root
restart: always
depends_on:
- - "pigpiod"
- network_mode: "host"
+ - 'pigpiod'
+ network_mode: 'host'
command: bash -c "modprobe i2c-dev && python3 -m ras.servos --config /config/driver.ini"
environment:
GPIOZERO_PIN_FACTORY: 'pigpio'
volumes:
-
- volume_local_config:/config:rw
+ #Bottom camera
stream0:
build:
context: .
@@ -50,11 +50,12 @@ services:
privileged: true
user: root
restart: always
- network_mode: "host"
- command: ["python3", "-m", "stream.camera", "--port", "9101", "--config", "/config/camera0.ini"]
+ network_mode: 'host'
+ command: ['python3', '-m', 'stream.camera', '--port', '9101', '--config', '/config/camera0.ini', '--ip-ending', '64']
stop_signal: SIGKILL
volumes:
- volume_local_config:/config:rw
+ #Upper camera
stream1:
build:
context: .
@@ -62,9 +63,8 @@ services:
privileged: true
user: root
restart: always
- network_mode: "host"
- command: ["python3", "-m", "stream.camera", "--port", "9102", "--config", "/config/camera1.ini"]
+ network_mode: 'host'
+ command: ['python3', '-m', 'stream.camera', '--port', '9102', '--config', '/config/camera1.ini', '--ip-ending', '65']
stop_signal: SIGKILL
volumes:
- - volume_local_config:/config:rw
-
+ - volume_local_config:/config:rw
\ No newline at end of file
diff --git a/raspi/ras/servos.py b/raspi/ras/servos.py
index e638bfb3..70b805d7 100644
--- a/raspi/ras/servos.py
+++ b/raspi/ras/servos.py
@@ -73,7 +73,7 @@ def velocity(self):
raise NotImplementedError()
@abstractmethod
- def drive(self, steering, throttle):
+ def drive(self, steering, throttle, spin):
raise NotImplementedError()
@abstractmethod
@@ -104,7 +104,7 @@ def is_configured(self):
def velocity(self):
return 0
- def drive(self, steering, throttle):
+ def drive(self, steering, throttle, spin):
return 0
def quit(self):
@@ -200,7 +200,7 @@ def is_configured(self):
def velocity(self):
raise NotImplementedError()
- def drive(self, steering, throttle):
+ def drive(self, steering, throttle, spin):
self._apply_steering(steering)
_motor_effort = 0
if self._motor_servo is not None:
@@ -263,7 +263,7 @@ def velocity(self):
logger.warning(e)
return 0
- def drive(self, steering, throttle):
+ def drive(self, steering, throttle, spin):
_motor_effort = self._throttle_config.get("scale") * throttle
_operational = self._drive.set_effort(_motor_effort)
if _operational:
@@ -345,7 +345,7 @@ def velocity(self):
logger.warning(e)
return 0
- def drive(self, steering, throttle):
+ def drive(self, steering, throttle, spin):
_motor_scale = self._throttle_config.get("scale")
# Scale down throttle for one wheel, the other retains its value.
steering = min(1.0, max(-1.0, steering + self._steering_offset))
@@ -355,8 +355,15 @@ def drive(self, steering, throttle):
right = throttle if steering < 0 else throttle * effect
a = (right if self._axes_ordered else left) * self._axis0_multiplier * _motor_scale
b = (left if self._axes_ordered else right) * self._axis1_multiplier * _motor_scale
- self._drive1.set_effort(a)
- self._drive2.set_effort(b)
+ if spin == 1 and abs(steering) <= 1e-9:
+ self._drive1.set_effort(-a)
+ self._drive2.set_effort(b)
+ elif spin == -1 and abs(steering) <= 1e-9:
+ self._drive1.set_effort(a)
+ self._drive2.set_effort(-b)
+ else:
+ self._drive1.set_effort(a)
+ self._drive2.set_effort(b)
return np.mean([a, b])
def quit(self):
@@ -487,6 +494,7 @@ def step(self):
v_steering = 0 if c_drive is None else c_drive.get("steering", 0)
v_throttle = 0 if c_drive is None else c_drive.get("throttle", 0)
v_wakeup = False if c_drive is None else bool(c_drive.get("wakeup"))
+ v_spin = None if c_drive is None else c_drive.get("spin", None)
self._cmd_history.touch(steering=v_steering, throttle=v_throttle, wakeup=v_wakeup)
if self._cmd_history.is_missing():
@@ -496,7 +504,7 @@ def step(self):
# Immediately zero out throttle when violations start occurring.
v_throttle = 0 if n_violations > 0 else v_throttle
- _effort = self._chassis.drive(v_steering, v_throttle)
+ _effort = self._chassis.drive(v_steering, v_throttle, v_spin)
_data = dict(time=timestamp(), configured=int(self._chassis.is_configured()), motor_effort=_effort)
if self._chassis.has_sensors():
_data.update(dict(velocity=self._chassis.velocity()))
diff --git a/raspi/stream/camera.py b/raspi/stream/camera.py
index 005c15a9..701791df 100644
--- a/raspi/stream/camera.py
+++ b/raspi/stream/camera.py
@@ -64,48 +64,32 @@ def step(self):
}
-def change_segment_config(config_dir):
+def change_segment_config(config_file, ip_ending):
"""Change the ips in all the config files the segment is using them.
It will count on the ip of the pi"""
# Get the local IP address's third octet
- ip_address = subprocess.check_output("hostname -I | awk '{for (i=1; i<=NF; i++) if ($i ~ /^192\\.168\\./) print $i}'", shell=True).decode().strip().split()[0]
- third_octet_new = ip_address.split(".")[2]
- # Regular expression to match IP addresses
- ip_regex = re.compile(r"(\d+\.\d+\.)(\d+)(\.\d+)")
+ ip_address = subprocess.check_output("hostname -I | awk '{for (i=1; i<=NF; i++) if ($i ~ /^192\\.168\\./) print $i}'", shell=True).decode().strip().split()[0]
+ base_ip = ip_address.rsplit(".", 1)[0] # Get the base IP without the last octet
+ new_ip = f"{base_ip}.{ip_ending}" # Form the new IP with the custom ending
- with open(config_dir, "r") as f:
- content = f.readlines()
+ with open(config_file, "r") as file:
+ content = file.readlines()
updated_content = []
- changes_made = []
- changes_made_in_file = False # Flag to track changes in the current file
+ ip_regex = re.compile(r"camera.ip = 192\.168\.\d+\.\d+")
for line in content:
- match = ip_regex.search(line)
- if match:
- third_octet_old = match.group(2)
- if third_octet_old != third_octet_new:
- # Replace the third octet
- new_line = ip_regex.sub(r"\g<1>" + third_octet_new + r"\g<3>", line)
- updated_content.append(new_line)
- changes_made.append((third_octet_old, third_octet_new))
- changes_made_in_file = True
-
- continue
- updated_content.append(line)
+ new_line = ip_regex.sub(f"camera.ip = {new_ip}", line)
+ updated_content.append(new_line)
- # Write changes back to the file
- with open(config_dir, "w") as f:
- f.writelines(updated_content)
+ with open(config_file, "w") as file:
+ file.writelines(updated_content)
- # Print changes made
- if changes_made_in_file:
- logger.info("Updated {} with a new ip address of {}".format(config_dir, third_octet_new))
+ logger.info(f"Updated {config_file} to new IP address {new_ip}")
def create_stream(config_file):
- change_segment_config(config_file)
parser = SafeConfigParser()
parser.read(config_file)
kwargs = dict(parser.items("camera"))
@@ -150,40 +134,44 @@ def create_stream(config_file):
def main():
parser = argparse.ArgumentParser(description="Camera web-socket server.")
- parser.add_argument("--config", type=str, default="/config/stream.ini", help="Configuration file.")
- parser.add_argument("--port", type=int, default=9101, help="Socket port.")
+ parser.add_argument("--config", type=str, required=True, help="Configuration file.")
+ parser.add_argument("--port", type=int, default=9101, help="Port for the web server.")
+ parser.add_argument("--ip-ending", type=str, required=True, help="Last octet of the IP address.")
args = parser.parse_args()
config_file = args.config
- if os.path.exists(config_file) and os.path.isfile(config_file):
- video_stream, socket_type = create_stream(config_file)
- application = CameraApplication(stream=video_stream, event=quit_event)
-
- threads = [threading.Thread(target=application.run)]
- if quit_event.is_set():
- return 0
-
- [t.start() for t in threads]
-
- asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy())
- asyncio.set_event_loop(asyncio.new_event_loop())
-
- io_loop = ioloop.IOLoop.instance()
- class_ref = HttpLivePlayerVideoSocket if socket_type == "http-live" else JMuxerVideoStreamSocket
- web_app = web.Application([(r"/", class_ref, dict(video_source=video_stream, io_loop=io_loop))])
- rear_server = web.HTTPServer(web_app, xheaders=True)
- rear_server.bind(args.port)
- rear_server.start()
- logger.info("Web service started on port {}.".format(args.port))
- io_loop.start()
-
- logger.info("Waiting on threads to stop.")
- [t.join() for t in threads]
- else:
+ if not os.path.exists(config_file):
shutil.copyfile("/app/stream/camera.template", config_file)
- logger.info("Created a new camera configuration file from template.")
- while not quit_event.is_set():
- time.sleep(1)
+ logger.info(f"Created a new camera configuration file from template at {config_file}")
+
+ change_segment_config(config_file, args.ip_ending)
+
+ video_stream, socket_type = create_stream(config_file)
+ application = CameraApplication(stream=video_stream, event=quit_event)
+
+ threads = [threading.Thread(target=application.run)]
+ if quit_event.is_set():
+ return 0
+
+ [t.start() for t in threads]
+
+ asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy())
+ asyncio.set_event_loop(asyncio.new_event_loop())
+
+ io_loop = ioloop.IOLoop.instance()
+ class_ref = HttpLivePlayerVideoSocket if socket_type == "http-live" else JMuxerVideoStreamSocket
+ web_app = web.Application([(r"/", class_ref, dict(video_source=video_stream, io_loop=io_loop))])
+ rear_server = web.HTTPServer(web_app, xheaders=True)
+ rear_server.bind(args.port)
+ rear_server.start()
+ logger.info("Web service started on port {}.".format(args.port))
+ io_loop.start()
+
+ logger.info("Waiting on threads to stop.")
+ [t.join() for t in threads]
+
+ while not quit_event.is_set():
+ time.sleep(1)
if __name__ == "__main__":
diff --git a/raspi/stream/camera.template b/raspi/stream/camera.template
index 3a1452cb..e58f54ba 100644
--- a/raspi/stream/camera.template
+++ b/raspi/stream/camera.template
@@ -2,5 +2,3 @@
camera.type = h264/rtsp
camera.ip = 192.168.1.64
-#camera.type = raw/usb/h264/udp
-#camera.uri = /dev/video0
diff --git a/teleop/.DS_Store b/teleop/.DS_Store
new file mode 100644
index 00000000..ba38dbcd
Binary files /dev/null and b/teleop/.DS_Store differ
diff --git a/teleop/Dockerfile b/teleop/Dockerfile
index e2eb114e..ad98c27f 100644
--- a/teleop/Dockerfile
+++ b/teleop/Dockerfile
@@ -12,7 +12,7 @@ RUN apt-get update && apt-get install -y \
# /\ unzip utility
RUN python3 -m pip install -U pip
-RUN pip3 install pymongo tornado folium Flask flask_socketio paramiko user-agents pysnmp pyasn1 pyasn1-modules
+RUN pip3 install pymongo tornado folium Flask flask_socketio paramiko user-agents pysnmp pyasn1 pyasn1-modules py-spy
# Ignore deprecation problem from cryptography
#/usr/local/lib/python3.6/dist-packages/pymongo/pyopenssl_context.py:26: CryptographyDeprecationWarning: Python 3.6 is no longer supported by the Python core team. Therefore, support for it is deprecated in cryptography. The next release of cryptography will remove support for Python 3.6.
diff --git a/teleop/htm/static/CSS/mobileController.css b/teleop/htm/static/CSS/mobileController.css
index a590095a..c4c22728 100644
--- a/teleop/htm/static/CSS/mobileController.css
+++ b/teleop/htm/static/CSS/mobileController.css
@@ -12,24 +12,50 @@ canvas {
display: block;
}
+/* top input container START */
#mobile-controller-top-input-container {
position: fixed;
- width: 100%;
+ width: 80%;
display: flex;
+ left: 50%;
+ transform: translateX(-50%);
align-items: center;
justify-content: center;
flex-direction: column;
- margin: 1vh 0;
+ margin: 1.5rem 0 0.5rem 0;
+ box-sizing: border-box;
+}
+
+.input-container {
+ font-size: 14px;
+ display: flex;
+ align-items: center;
+ margin: 0.5rem 0;
+ width: 100%;
+ box-sizing: border-box;
}
-#dead-zone-container {
- margin: 1vh 0;
+.input-container label {
+ width: 110px;
+ flex-shrink: 0; /* Ensure label doesn't shrink */
+}
+
+.input-text {
+ width: 5ch;
+ margin-left: 15px;
+ display: inline-block;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ text-align: right; /* Align text to the right for a consistent look */
}
input[type=range] {
-webkit-appearance: none;
- width: 70%;
+ appearance: none;
+ flex-grow: 1; /* Allow the input to grow and fill available space */
margin: 10px 0;
+ box-sizing: border-box;
}
input[type=range]::-webkit-slider-runnable-track {
@@ -50,8 +76,17 @@ input[type=range]::-webkit-slider-thumb {
margin-top: -4px;
}
+/* Adjust for larger screens */
+@media (min-width: 768px) {
+ #mobile-controller-top-input-container {
+ width: 50%;
+ }
+}
+
+/* top input container END */
+
/* Adjusted styles for the container and option buttons to center them horizontally at the bottom of the page */
-.mobile_controller_button_container {
+#mobile-controller-bottom-input-container {
position: fixed;
bottom: 2vw;
left: 50%;
@@ -114,37 +149,33 @@ input[type=range]::-webkit-slider-thumb {
top: 20px;
}
-.scale-offset-input-containers {
+.scale-offset-confirm-text {
font-size: 14px;
- display: flex;
- align-items: center;
margin-left: 10px;
}
-.scale-offset-input-containers label {
- width: 110px;
-}
-
-.scale-offset-input-box-containers {
- display: flex;
- align-items: center;
-}
-
-.scale-input-text,
-.offset-input-text {
- width: 60px;
- margin-left: 15px;
- display: inline-block;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
+.hidden {
+ display: none;
}
-.scale-offset-confirm-text {
- font-size: 14px;
- margin-left: 10px;
-}
-.hidden {
+canvas#following_imageCanvas {
display: none;
+ /* Initially hidden */
+ position: fixed;
+ /* Fixed position to keep the canvas in the center of the viewport */
+ top: 50%;
+ /* Align center vertically */
+ left: 50%;
+ /* Align center horizontally */
+ transform: translate(-50%, -50%);
+ /* Adjust position to the exact center */
+ border: 1px solid black;
+ /* Optional: Adds a border around the canvas */
+ max-width: 640px;
+ /* Maximum width of the canvas */
+ max-height: 480px;
+ /* Maximum height of the canvas */
+ aspect-ratio: 4 / 3;
+ /* Maintain 4:3 aspect ratio */
}
\ No newline at end of file
diff --git a/teleop/htm/static/JS/mobileController/mobileController_a_app.js b/teleop/htm/static/JS/mobileController/mobileController_a_app.js
index d89d4d00..55aa5677 100644
--- a/teleop/htm/static/JS/mobileController/mobileController_a_app.js
+++ b/teleop/htm/static/JS/mobileController/mobileController_a_app.js
@@ -1,111 +1,132 @@
-import { InferenceToggleButton } from "./mobileController_b_shape_Inference.js"
-import { bottomTriangle, topTriangle } from "./mobileController_b_shape_triangle.js"
-import { detectTriangle, getSavedDeadZoneWidth, handleDotMove, handleTriangleMove, initializeWS, saveDeadZoneWidth, sendJSONCommand } from "./mobileController_c_logic.js"
-import { cursorFollowingDot } from "./mobileController_b_shape_dot.js";
-import { MotorDataInput } from "./mobileController_e_scale_offset_input.js";
-import { ToggleButtonHandler } from "./mobileController_b_shape_confidence.js"
-
-import { app, changeTrianglesColor, redraw } from "./mobileController_d_pixi.js"
+import { followingButtonHandler } from './mobileController_b_following.js';
+import { ToggleButtonHandler } from './mobileController_b_shape_confidence.js';
+import { cursorFollowingDot } from './mobileController_b_shape_dot.js';
+import { InferenceToggleButton } from './mobileController_b_shape_Inference.js';
+import { bottomTriangle, topTriangle } from './mobileController_b_shape_triangle.js';
+import { detectTriangle, getSavedDeadZoneWidth, handleDotMove, handleTriangleMove, initializeWS, saveDeadZoneWidth, sendJSONCommand } from './mobileController_c_logic.js';
+import { app, changeTrianglesColor, redraw, removeTriangles } from './mobileController_d_pixi.js';
+import { MotorDataInput } from './mobileController_e_scale_offset_input.js';
import CTRL_STAT from './mobileController_z_state.js'; // Stands for control state
// Initialize sending commands only once, instead of calling it each time we touch the triangles
sendJSONCommand();
MotorDataInput.showInputElements();
let intervalId;
-let inferenceToggleButton
+let inferenceToggleButton;
+let deadZoneText = document.getElementById('deadZone-input-text');
window.addEventListener('load', () => {
- initializeWS()
- inferenceToggleButton = new InferenceToggleButton("inference_toggle_button")
- changeTrianglesColor(0x000000)
- new ToggleButtonHandler('confidenceToggleButton')
- let deadZoneSlider = document.getElementById('deadZoneWidth');
- deadZoneSlider.value = getSavedDeadZoneWidth(); // Initialize slider with saved value
+ initializeWS();
+ inferenceToggleButton = new InferenceToggleButton('inference_toggle_button');
+ changeTrianglesColor(0x000000);
+ new ToggleButtonHandler('confidenceToggleButton');
+ let deadZoneSlider = document.getElementById('deadZoneWidth');
+ deadZoneSlider.value = getSavedDeadZoneWidth(); // Initialize slider with saved value
+ deadZoneText.textContent = getSavedDeadZoneWidth() * 2;
});
+// This function should be moved to e_scale file. Will be done in a different branch
// Dead zone width slider input event listener
document.getElementById('deadZoneWidth').addEventListener('input', function () {
- let value = this.value;
- // Save the new dead zone width to local storage after handling the dot move
- saveDeadZoneWidth(value);
+ let value = this.value;
+ deadZoneText.textContent = value * 2;
+ // Save the new dead zone width to local storage after handling the dot move
+ saveDeadZoneWidth(value);
});
window.addEventListener('resize', () => {
- app.renderer.resize(window.innerWidth, window.innerHeight);
- topTriangle.updateDimensions();
- bottomTriangle.updateDimensions();
- redraw();
+ // To not show the triangles if the stream canvas is shown
+ if (CTRL_STAT.followingState != 'active') {
+ app.renderer.resize(window.innerWidth, window.innerHeight);
+ topTriangle.updateDimensions();
+ bottomTriangle.updateDimensions();
+ redraw();
+ }
});
app.view.addEventListener('touchstart', (event) => {
- CTRL_STAT.initialYOffset = event.touches[0].clientY - window.innerHeight / 2; // Calculate the initial Y offset
- detectTriangle(event.touches[0].clientX, event.touches[0].clientY);
- //if condition to make sure it will move only if the user clicks inside one of the two triangles
- if (CTRL_STAT.detectedTriangle !== 'none') {
- switch (CTRL_STAT.stateErrors) {
- case "controlError":
- console.error("Another user has connected. Refresh the page to take control back");
- break;
- case "connectionError":
- console.error("Connection lost with the robot. Please reconnect");
- break;
- default:
- document.getElementById("mobile-controller-top-input-container").style.display = "none";
- document.getElementById("mobile-controller-bottom-input-container").style.display = "none";
- startOperating(event)
- app.view.addEventListener('touchmove', onTouchMove);
- break;
- }
- } else {
- console.error('Clicked outside the triangles. click inside one of the two triangles to start.');
- }
+ CTRL_STAT.initialYOffset = event.touches[0].clientY - window.innerHeight / 2; // Calculate the initial Y offset
+ detectTriangle(event.touches[0].clientX, event.touches[0].clientY);
+ //Make sure it will move only if the user clicks inside one of the two triangles
+ if (CTRL_STAT.detectedTriangle !== 'none') {
+ switch (CTRL_STAT.stateErrors) {
+ case 'controlError':
+ console.error('Another user has connected. Refresh the page to take control back');
+ break;
+ case 'connectionError':
+ console.error('Connection lost with the robot. Please reconnect');
+ break;
+ default:
+ document.getElementById('mobile-controller-top-input-container').style.display = 'none';
+ document.getElementById('mobile-controller-bottom-input-container').style.display = 'none';
+ if (CTRL_STAT.followingState == 'active') {
+ removeTriangles();
+ } else {
+ startOperating(event);
+ app.view.addEventListener('touchmove', onTouchMove);
+ }
+ break;
+ }
+ } else {
+ console.error('Clicked outside the triangles. click inside one of the two triangles to start.');
+ }
});
-
function startOperating(event) {
- cursorFollowingDot.show()
- handleDotMove(event.touches[0].clientX, event.touches[0].clientY, inferenceToggleButton.getInferenceState);
- handleTriangleMove(event.touches[0].clientY, inferenceToggleButton);
- if (inferenceToggleButton.getInferenceState == "train") {
- document.getElementById('inference_options_container').style.display = 'none';
- }
+ // Hide the button when triangles are pressed
+ followingButtonHandler.setStyle('display', 'none');
+ cursorFollowingDot.show();
+ handleDotMove(event.touches[0].clientX, event.touches[0].clientY, inferenceToggleButton.getInferenceState);
+ handleTriangleMove(event.touches[0].clientY, inferenceToggleButton);
+ if (inferenceToggleButton.getInferenceState == 'train') {
+ document.getElementById('inference_options_container').style.display = 'none';
+ }
}
function onTouchMove(event) {
- event.preventDefault(); // Prevent scrolling while moving the triangles
- if (inferenceToggleButton.getInferenceState == "train") {
- document.getElementById('inference_options_container').style.display = 'none';
- }
- // Update the dot's position
- handleDotMove(event.touches[0].clientX, event.touches[0].clientY, inferenceToggleButton.getInferenceState);
+ event.preventDefault(); // Prevent scrolling while moving the triangles
+ if (inferenceToggleButton.getInferenceState == 'train') {
+ document.getElementById('inference_options_container').style.display = 'none';
+ }
+ // Update the dot's position
+ handleDotMove(event.touches[0].clientX, event.touches[0].clientY, inferenceToggleButton.getInferenceState);
}
-
app.view.addEventListener('touchend', () => {
- //So it call the redraw function on the triangles or dot which may not have moved (due to user clicking outside the triangles)
- if (CTRL_STAT.detectedTriangle !== 'none') {
- if (inferenceToggleButton.getInferenceState != "true") {
- document.getElementById("mobile-controller-top-input-container").style.display = "flex";
- document.getElementById("mobile-controller-bottom-input-container").style.display = "flex";
+ // it should keep hiding the triangles if the fol is still working
- redraw(); // Reset triangles to their original position
+ //So it call the redraw function on the triangles or dot which may not have moved (due to user clicking outside the triangles)
+ if (CTRL_STAT.detectedTriangle !== 'none') {
+ if (inferenceToggleButton.getInferenceState != 'true') {
+ document.getElementById('mobile-controller-top-input-container').style.display = 'flex';
+ document.getElementById('mobile-controller-bottom-input-container').style.display = 'flex';
- CTRL_STAT.selectedTriangle = null; // Reset the selected triangle
- app.view.removeEventListener('touchmove', onTouchMove); //remove the connection to save CPU
- CTRL_STAT.throttleSteeringJson = { steering: 0, throttle: 0 }; // send the stopping signal for the motors
- // so it doesn't show the div when in inference mode
- if (inferenceToggleButton.getInferenceState == "false") {
- document.getElementById('toggle_button_container').style.display = 'flex';
- }
- if (inferenceToggleButton.getInferenceState == "train") {
- document.getElementById('inference_options_container').style.display = 'flex';
- redraw(undefined, true, false, true)
- }
- cursorFollowingDot.hide()
- clearTimeout(intervalId);
- }
+ redraw(); // Reset triangles to their original position
+ if (CTRL_STAT.followingState === 'active') {
+ removeTriangles();
+ document.getElementById('mobile-controller-top-input-container').style.display = 'none';
+ document.getElementById('inference_toggle_button').style.display = 'none';
+ document.getElementById('confidenceToggleButton').style.display = 'none';
+ }
- // Making the text boxes show the current data that exist on the robot
- MotorDataInput.showInputElements();
- }
-});
\ No newline at end of file
+ CTRL_STAT.selectedTriangle = null; // Reset the selected triangle
+ app.view.removeEventListener('touchmove', onTouchMove); //remove the connection to save CPU
+ CTRL_STAT.throttleSteeringJson = { steering: 0, throttle: 0 }; // send the stopping signal for the motors
+ // so it doesn't show the div when in inference mode
+ if (inferenceToggleButton.getInferenceState == 'false') {
+ document.getElementById('toggle_button_container').style.display = 'flex';
+ }
+ if (inferenceToggleButton.getInferenceState == 'train') {
+ document.getElementById('inference_options_container').style.display = 'flex';
+ redraw(undefined, true, false, true);
+ }
+ cursorFollowingDot.hide();
+ clearTimeout(intervalId);
+ }
+ // Show the button again when touch ends
+ followingButtonHandler.setStyle('display', 'block');
+
+ // Making the text boxes show the current data that exist on the robot
+ MotorDataInput.showInputElements();
+ }
+});
diff --git a/teleop/htm/static/JS/mobileController/mobileController_b_following.js b/teleop/htm/static/JS/mobileController/mobileController_b_following.js
new file mode 100644
index 00000000..e3935070
--- /dev/null
+++ b/teleop/htm/static/JS/mobileController/mobileController_b_following.js
@@ -0,0 +1,172 @@
+import { redraw, removeTriangles } from './mobileController_d_pixi.js';
+import CTRL_STAT from './mobileController_z_state.js';
+
+class ToggleButtonHandler {
+ constructor(buttonId) {
+ this.toggleButton = document.getElementById(buttonId);
+ this.topInputDiv = document.getElementById('mobile-controller-top-input-container');
+ this.inferenceToggleBtn = document.getElementById('inference_toggle_button');
+ this.confidenceToggleBtn = document.getElementById('confidenceToggleButton');
+ this.canvas = document.getElementById('following_imageCanvas');
+ this.ctx = this.canvas.getContext('2d');
+ this.errorLogged = false; // Add this line to initialize the error flag
+
+ this.initialSetup();
+ this.startPolling();
+ }
+
+ initialSetup() {
+ this.resizeCanvas();
+ window.addEventListener('resize', () => this.resizeCanvas());
+ this.toggleButton.addEventListener('click', () => this.handleFollowingToggleButtonClick());
+ }
+
+ handleFollowingToggleButtonClick() {
+ if (CTRL_STAT.followingState == 'inactive') {
+ this.sendSwitchFollowingRequest('start_following');
+ } else if (CTRL_STAT.followingState == 'active') {
+ this.sendSwitchFollowingRequest('stop_following');
+ }
+ }
+
+ sendSwitchFollowingRequest(command) {
+ fetch('/switch_following', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ body: `command=${encodeURIComponent(command)}`,
+ })
+ .then((response) => response.json())
+ .catch((error) => console.error('Error sending command:', error));
+ }
+
+ startPolling() {
+ setInterval(() => {
+ fetch('/switch_following_status', {
+ method: 'GET',
+ headers: { 'Content-Type': 'application/json' },
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ const previousState = CTRL_STAT.followingState;
+ this.assignFollowingState(data.following_status);
+ // Only call this function if the current state changed from the received one
+ if (previousState !== CTRL_STAT.followingState) {
+ this.toggleButtonAppearance();
+ }
+ this.errorLogged = false; // Reset the error flag if request is successful
+ })
+ .catch((error) => {
+ if (!this.errorLogged) {
+ console.error('Error polling backend:', error);
+ this.errorLogged = true; // Set the error flag
+ }
+ });
+ }, 500);
+ }
+
+ assignFollowingState(backendCommand) {
+ // console.log(backendCommand, this._followingState)
+ switch (backendCommand) {
+ case 'active':
+ CTRL_STAT.followingState = 'active'; // The system is actively following
+ break;
+ case 'inactive':
+ CTRL_STAT.followingState = 'inactive'; // The system is ready and not following
+ break;
+ case 'loading':
+ CTRL_STAT.followingState = 'loading'; // The system is loading
+ break;
+ default:
+ console.log('Following: Unknown command received from the backend:', backendCommand);
+ }
+ }
+
+ toggleButtonAppearance() {
+ if (CTRL_STAT.followingState == 'active') {
+ removeTriangles();
+ this.showCanvas();
+ this.controlInputControllerVisibility('none');
+ this.toggleButton.innerText = 'Stop Following';
+ this.toggleButton.style.backgroundColor = '#ff6347';
+ } else if (CTRL_STAT.followingState == 'inactive') {
+ redraw(undefined, true, true, true);
+ this.hideCanvas();
+ this.controlInputControllerVisibility('flex');
+ this.toggleButton.innerText = 'Start Following';
+ this.toggleButton.style.backgroundColor = '#67b96a';
+ } else if (CTRL_STAT.followingState == 'loading') {
+ this.hideCanvas();
+ this.controlInputControllerVisibility('flex');
+ this.toggleButton.innerText = 'Loading...';
+ this.toggleButton.style.backgroundColor = '#ffa500';
+ }
+ }
+
+ controlInputControllerVisibility(command) {
+ this.topInputDiv.style.display = command;
+ this.inferenceToggleBtn.style.display = command;
+ this.confidenceToggleBtn.style.display = command;
+ }
+
+ showCanvas() {
+ this.canvas.style.display = 'block';
+ if (!this.streamActive && !this.intervalId) {
+ this.streamActive = true;
+ this.intervalId = setInterval(() => this.refreshImage(), 150); // Start streaming
+ }
+ }
+
+ hideCanvas() {
+ this.canvas.style.display = 'none';
+ if (this.streamActive && this.intervalId) {
+ clearInterval(this.intervalId); // Stop streaming
+ this.intervalId = null;
+ this.streamActive = false;
+ }
+ }
+
+ refreshImage() {
+ if (!this.streamActive) return; // Do not proceed if streaming is not active
+
+ const img = new Image();
+ img.onload = () => {
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); // Clear previous image
+ this.ctx.drawImage(img, 0, 0, this.canvas.width, this.canvas.height); // Draw new image
+ };
+ img.src = '/latest_image?' + new Date().getTime(); // Include cache busting to prevent loading from cache
+ }
+
+ resizeCanvas() {
+ if (CTRL_STAT.followingState == 'active') {
+ removeTriangles();
+ }
+ let maxWidth = window.innerWidth * 0.8; // 80% of the viewport width
+ if (maxWidth > 640) maxWidth = 640; // Ensuring the width does not exceed 640 pixels
+ const maxHeight = (maxWidth * 3) / 4; // Maintain 4:3 ratio
+
+ this.canvas.width = maxWidth;
+ this.canvas.height = maxHeight;
+ this.canvas.style.width = `${maxWidth}px`;
+ this.canvas.style.height = `${maxHeight}px`;
+ }
+
+ getAttribute(attributeName) {
+ return this.toggleButton.getAttribute(attributeName);
+ }
+
+ setAttribute(attributeName, value) {
+ this.toggleButton.setAttribute(attributeName, value);
+ }
+
+ getStyle(property) {
+ return this.toggleButton.style[property];
+ }
+
+ setStyle(property, value) {
+ this.toggleButton.style[property] = value;
+ }
+}
+
+const followingButtonHandler = new ToggleButtonHandler('following_toggle_button');
+
+export { followingButtonHandler };
diff --git a/teleop/htm/static/JS/mobileController/mobileController_b_shape_Inference.js b/teleop/htm/static/JS/mobileController/mobileController_b_shape_Inference.js
index 3bff4a96..2f803f8e 100644
--- a/teleop/htm/static/JS/mobileController/mobileController_b_shape_Inference.js
+++ b/teleop/htm/static/JS/mobileController/mobileController_b_shape_Inference.js
@@ -1,226 +1,217 @@
-import { addKeyToSentCommand } from "./mobileController_c_logic.js"
+import { addKeyToSentCommand } from './mobileController_c_logic.js';
import { redraw, removeTriangles, changeTrianglesColor } from './mobileController_d_pixi.js';
-import { topTriangle, bottomTriangle } from "./mobileController_b_shape_triangle.js"
-
+import { topTriangle, bottomTriangle } from './mobileController_b_shape_triangle.js';
/**
* Handles toggle button interactions and manages WebSocket connections for real-time data updates.
*/
class InferenceToggleButton {
- constructor(buttonId) {
- this.toggleButton = document.getElementById(buttonId);
- this.optionsContainer = document.getElementById('inference_options_container');
- this.toggleButtonContainer = document.getElementById('toggle_button_container');
- this.inferenceTrainingButton = document.getElementById('inference_training_toggle');
- this.InferenceAutoNavigationToggle = document.getElementById('inference_auto_navigation_toggle');
- this.InferenceAutoSpeedText = document.getElementById('inference_auto_speed');
- this.hideOptionsButton = document.getElementById('hide_options');
- this.speedElement = document.getElementById("inference_auto_speed");
-
- this.logWS = {}; // Placeholder for WebSocket.
- this.logWSmessage;
- this.autoReconnectInterval = 9000;
- this.initializeLogWS();
- /*
+ constructor(buttonId) {
+ this.toggleButton = document.getElementById(buttonId);
+ this.optionsContainer = document.getElementById('inference_options_container');
+ this.toggleButtonContainer = document.getElementById('toggle_button_container');
+ this.inferenceTrainingButton = document.getElementById('inference_training_toggle');
+ this.InferenceAutoNavigationToggle = document.getElementById('inference_auto_navigation_toggle');
+ this.InferenceAutoSpeedText = document.getElementById('inference_auto_speed');
+ this.hideOptionsButton = document.getElementById('hide_options');
+ this.speedElement = document.getElementById('inference_auto_speed');
+
+ this.logWS = {}; // Placeholder for WebSocket.
+ this.logWSmessage;
+ this.autoReconnectInterval = 9000;
+ this.initializeLogWS();
+ /*
Means no smoothing for the other classes
false == working on normal mode
true == Inference is working on mobile controller
train == Inference is on training mode
- auto == Inference is on training mode
+ auto == Inference is on auto navigation mode
*/
- this._inferenceState = "false";
- this.currentAutoSpeed = 0
- this.buttonsEventListener()
- }
-
- get getInferenceState() {
- return this._inferenceState;
- }
-
- buttonsEventListener() {
- this.toggleButton.addEventListener('click', () => this.showInferenceOptions());
- this.hideOptionsButton.addEventListener('click', () => this.hideInferenceOptions());
- this.inferenceTrainingButton.addEventListener('click', () => this.handleInferenceTrainClick());
- this.InferenceAutoNavigationToggle.addEventListener('click', () => this.handleAutoNavigationClick());
- }
-
- hideInferenceOptions() {
- this.InferenceAutoSpeedText.style.display = "none"
- this.optionsContainer.style.display = 'none';
- this.toggleButtonContainer.style.display = 'flex';
- // Switch to driver_mode.teleop.direct mode
- addKeyToSentCommand("button_b", 1)
- // Turn off all the buttons to start state
- if (this._inferenceState == "auto") {
- this.handleAutoNavigationClick()
- this._inferenceState = "false"
- } else if (this._inferenceState == "train") {
- this.handleInferenceTrainClick()
- this._inferenceState = "false"
- }
- //Make it start with dark colour
- redraw(undefined, true, true, true);
- changeTrianglesColor(0x000000)
- this._inferenceState = "false"
- }
-
- showInferenceOptions() {
- removeTriangles()
- this.optionsContainer.style.display = 'flex';
- this.toggleButtonContainer.style.display = 'none';
- this._inferenceState = "true"
- }
-
- handleAutoNavigationClick() {
- this._inferenceState = this._inferenceState === "auto" ? "true" : "auto";
- // Now decide what to do based on the new state
- if (this._inferenceState === "auto") {
- this.startAutoNavigation();
- } else {
- this.stopAutoNavigation();
- }
- }
-
- startAutoNavigation() {
- this.InferenceAutoNavigationToggle.innerText = "Stop Auto-navigation";
- redraw(undefined, true, true, false);
- addKeyToSentCommand("button_y", 1);
- topTriangle.changeText("Raise Speed", 25);
- bottomTriangle.changeText("Lower Speed", 25);
- this.InferenceAutoSpeedText.style.display = "block";
- this.hideOptionsButton.innerText = "Go to manual mode";
- this.inferenceTrainingButton.style.display = "none";
- }
-
- stopAutoNavigation() {
- this.InferenceAutoNavigationToggle.innerText = "Start Auto-navigation";
- removeTriangles();
- addKeyToSentCommand("button_b", 1);
- this.InferenceAutoSpeedText.style.display = "none";
- this.inferenceTrainingButton.style.display = "flex";
- this.hideOptionsButton.innerText = "Hide Options";
- this.speedElement.innerHTML = `0 Km/h`;
- }
-
- handleInferenceTrainClick() {
- this._inferenceState = this._inferenceState === "train" ? "true" : "train";
- if (this._inferenceState === "train") {
- this.startTraining();
- } else {
- this.stopTraining();
- }
- }
-
- startTraining() {
- this.inferenceTrainingButton.innerText = "Stop Training";
- redraw(undefined, true, false, true);
- addKeyToSentCommand("button_y", 1);
- this.hideOptionsButton.innerText = "Go to manual mode";
- this.InferenceAutoNavigationToggle.style.display = "none";
- }
-
- stopTraining() {
- this.inferenceTrainingButton.innerText = "Start Training";
- removeTriangles();
- addKeyToSentCommand("button_b", 1);
- this.hideOptionsButton.innerText = "Hide Options";
- this.InferenceAutoNavigationToggle.style.display = "flex";
- }
-
- /**
- * Send message to increase to decrease the autopilot mode
- * @param {string} touchedTriangle - name of the triangle selected
- */
- handleSpeedControl(touchedTriangle) {
- // Retrieve the current speed from the element
- // Round the received number to the nearest 0.5 for consistency
- let roundedSpeed = Math.round(this.logWSmessage.max_speed * 2) / 2;
- if (!this.logWSmessage._is_on_autopilot) {
- addKeyToSentCommand("button_y", 1);
- console.log(this.logWSmessage._is_on_autopilot)
- }
- // Update the speed display, ensuring it always has one decimal place
- this.speedElement.innerHTML = `${roundedSpeed.toFixed(1)} Km/h`;
- if (touchedTriangle == "top" && roundedSpeed < 6) {
- addKeyToSentCommand("arrow_up", 1);
- } else if (touchedTriangle == "bottom" && roundedSpeed > 0) {
- addKeyToSentCommand("arrow_down", 1);
- }
- }
-
-
- initializeLogWS() {
- let WSprotocol = document.location.protocol === 'https:' ? 'wss://' : 'ws://';
- let WSurl = `${WSprotocol}${document.location.hostname}:${document.location.port}/ws/log`;
- this.logWS.websocket = new WebSocket(WSurl);
- this.errorCount = 0; // Initialize error count
-
- this.logWS.websocket.onopen = (event) => {
- console.log('Log WS connection opened');
- this.logWS.isWebSocketOpen = true;
- this.errorCount = 0; // Reset error count on successful connection
- this.sendInterval = setInterval(() => {
- if (this.logWS.websocket.readyState === WebSocket.OPEN) {
- this.logWS.websocket.send('{}');
- } else {
- clearInterval(this.sendInterval); // Clear interval if not open
- }
- }, 40);
- };
-
- this.logWS.websocket.onmessage = (event) => {
- let jsonLogWSmessage = JSON.parse(event.data);
- this.decorate_server_message(jsonLogWSmessage)
- // console.log('Log WS:', this.logWSmessage);
-
- if (this.logWSmessage._is_on_autopilot
- && this.logWSmessage._has_passage == false
- && this._inferenceState == "auto") {
- changeTrianglesColor(0xFF0000)
- }
-
- };
-
- this.logWS.websocket.onerror = (error) => {
- if (this.errorCount < 5) {
- console.error('WebSocket Error:', error);
- this.errorCount++;
- }
- };
-
- this.logWS.websocket.onclose = (event) => {
- console.log('Log WS connection closed');
- this.logWS.isWebSocketOpen = false;
- clearInterval(this.sendInterval); // Ensure interval is cleared on close
- // Automatically try to reconnect after a specified interval
- setTimeout(() => this.checkAndReconnectWebSocket(), this.autoReconnectInterval);
- };
-
-
- }
-
- /**
- * Add fields related to INF state to the message
- * @param {json} message Message received from log endpoint
- */
- decorate_server_message(message) {
- message._is_on_autopilot = message.ctl == 5;
- message._has_passage = message.inf_total_penalty < 1;
- if (message.geo_head == undefined) {
- message.geo_head_text = 'n/a';
- } else {
- message.geo_head_text = message.geo_head.toFixed(2);
- }
- this.logWSmessage = message
- }
-
- checkAndReconnectWebSocket() {
- if (!this.logWS.websocket || this.logWS.websocket.readyState === WebSocket.CLOSED) {
- this.initializeLogWS();
- }
- }
-
+ this._inferenceState = 'false';
+ this.currentAutoSpeed = 0;
+ this.buttonsEventListener();
+ }
+
+ get getInferenceState() {
+ return this._inferenceState;
+ }
+
+ buttonsEventListener() {
+ this.toggleButton.addEventListener('click', () => this.showInferenceOptions());
+ this.hideOptionsButton.addEventListener('click', () => this.hideInferenceOptions());
+ this.inferenceTrainingButton.addEventListener('click', () => this.handleInferenceTrainClick());
+ this.InferenceAutoNavigationToggle.addEventListener('click', () => this.handleAutoNavigationClick());
+ }
+
+ hideInferenceOptions() {
+ this.InferenceAutoSpeedText.style.display = 'none';
+ this.optionsContainer.style.display = 'none';
+ this.toggleButtonContainer.style.display = 'flex';
+ // Switch to driver_mode.teleop.direct mode
+ addKeyToSentCommand('button_b', 1);
+ // Turn off all the buttons to start state
+ if (this._inferenceState == 'auto') {
+ this.handleAutoNavigationClick();
+ this._inferenceState = 'false';
+ } else if (this._inferenceState == 'train') {
+ this.handleInferenceTrainClick();
+ this._inferenceState = 'false';
+ }
+ //Make it start with dark colour
+ redraw(undefined, true, true, true);
+ changeTrianglesColor(0x000000);
+ this._inferenceState = 'false';
+ }
+
+ showInferenceOptions() {
+ removeTriangles();
+ this.optionsContainer.style.display = 'flex';
+ this.toggleButtonContainer.style.display = 'none';
+ this._inferenceState = 'true';
+ }
+
+ handleAutoNavigationClick() {
+ this._inferenceState = this._inferenceState === 'auto' ? 'true' : 'auto';
+ // Now decide what to do based on the new state
+ if (this._inferenceState === 'auto') {
+ this.startAutoNavigation();
+ } else {
+ this.stopAutoNavigation();
+ }
+ }
+
+ startAutoNavigation() {
+ this.InferenceAutoNavigationToggle.innerText = 'Stop Auto-navigation';
+ redraw(undefined, true, true, false);
+ addKeyToSentCommand('button_y', 1);
+ topTriangle.changeText('Raise Speed', 25);
+ bottomTriangle.changeText('Lower Speed', 25);
+ this.InferenceAutoSpeedText.style.display = 'block';
+ this.hideOptionsButton.innerText = 'Go to manual mode';
+ this.inferenceTrainingButton.style.display = 'none';
+ }
+
+ stopAutoNavigation() {
+ this.InferenceAutoNavigationToggle.innerText = 'Start Auto-navigation';
+ removeTriangles();
+ addKeyToSentCommand('button_b', 1);
+ this.InferenceAutoSpeedText.style.display = 'none';
+ this.inferenceTrainingButton.style.display = 'flex';
+ this.hideOptionsButton.innerText = 'Hide Options';
+ this.speedElement.innerHTML = `0 Km/h`;
+ }
+
+ handleInferenceTrainClick() {
+ this._inferenceState = this._inferenceState === 'train' ? 'true' : 'train';
+ if (this._inferenceState === 'train') {
+ this.startTraining();
+ } else {
+ this.stopTraining();
+ }
+ }
+
+ startTraining() {
+ this.inferenceTrainingButton.innerText = 'Stop Training';
+ redraw(undefined, true, false, true);
+ addKeyToSentCommand('button_y', 1);
+ this.hideOptionsButton.innerText = 'Go to manual mode';
+ this.InferenceAutoNavigationToggle.style.display = 'none';
+ }
+
+ stopTraining() {
+ this.inferenceTrainingButton.innerText = 'Start Training';
+ removeTriangles();
+ addKeyToSentCommand('button_b', 1);
+ this.hideOptionsButton.innerText = 'Hide Options';
+ this.InferenceAutoNavigationToggle.style.display = 'flex';
+ }
+
+ /**
+ * Send message to increase to decrease the autopilot mode
+ * @param {string} touchedTriangle - name of the triangle selected
+ */
+ handleSpeedControl(touchedTriangle) {
+ // Retrieve the current speed from the element
+ // Round the received number to the nearest 0.5 for consistency
+ let roundedSpeed = Math.round(this.logWSmessage.max_speed * 2) / 2;
+ if (!this.logWSmessage._is_on_autopilot) {
+ addKeyToSentCommand('button_y', 1);
+ console.log(this.logWSmessage._is_on_autopilot);
+ }
+ // Update the speed display, ensuring it always has one decimal place
+ this.speedElement.innerHTML = `${roundedSpeed.toFixed(1)} Km/h`;
+ if (touchedTriangle == 'top' && roundedSpeed < 6) {
+ addKeyToSentCommand('arrow_up', 1);
+ } else if (touchedTriangle == 'bottom' && roundedSpeed > 0) {
+ addKeyToSentCommand('arrow_down', 1);
+ }
+ }
+
+ initializeLogWS() {
+ let WSprotocol = document.location.protocol === 'https:' ? 'wss://' : 'ws://';
+ let WSurl = `${WSprotocol}${document.location.hostname}:${document.location.port}/ws/log`;
+ this.logWS.websocket = new WebSocket(WSurl);
+ this.errorCount = 0; // Initialize error count
+
+ this.logWS.websocket.onopen = (event) => {
+ console.log('Log WS connection opened');
+ this.logWS.isWebSocketOpen = true;
+ this.errorCount = 0; // Reset error count on successful connection
+ this.sendInterval = setInterval(() => {
+ if (this.logWS.websocket.readyState === WebSocket.OPEN) {
+ this.logWS.websocket.send('{}');
+ } else {
+ clearInterval(this.sendInterval); // Clear interval if not open
+ }
+ }, 40);
+ };
+
+ this.logWS.websocket.onmessage = (event) => {
+ let jsonLogWSmessage = JSON.parse(event.data);
+ this.decorate_server_message(jsonLogWSmessage);
+ // console.log('Log WS:', this.logWSmessage);
+
+ if (this.logWSmessage._is_on_autopilot && this.logWSmessage._has_passage == false && this._inferenceState == 'auto') {
+ changeTrianglesColor(0xff0000);
+ }
+ };
+
+ this.logWS.websocket.onerror = (error) => {
+ if (this.errorCount < 5) {
+ console.error('WebSocket Error:', error);
+ this.errorCount++;
+ }
+ };
+
+ this.logWS.websocket.onclose = (event) => {
+ console.log('Log WS connection closed');
+ this.logWS.isWebSocketOpen = false;
+ clearInterval(this.sendInterval); // Ensure interval is cleared on close
+ // Automatically try to reconnect after a specified interval
+ setTimeout(() => this.checkAndReconnectWebSocket(), this.autoReconnectInterval);
+ };
+ }
+
+ /**
+ * Add fields related to INF state to the message
+ * @param {json} message Message received from log endpoint
+ */
+ decorate_server_message(message) {
+ message._is_on_autopilot = message.ctl == 5;
+ message._has_passage = message.inf_total_penalty < 1;
+ if (message.geo_head == undefined) {
+ message.geo_head_text = 'n/a';
+ } else {
+ message.geo_head_text = message.geo_head.toFixed(2);
+ }
+ this.logWSmessage = message;
+ }
+
+ checkAndReconnectWebSocket() {
+ if (!this.logWS.websocket || this.logWS.websocket.readyState === WebSocket.CLOSED) {
+ this.initializeLogWS();
+ }
+ }
}
-
-export { InferenceToggleButton };
\ No newline at end of file
+export { InferenceToggleButton };
diff --git a/teleop/htm/static/JS/mobileController/mobileController_b_shape_confidence.js b/teleop/htm/static/JS/mobileController/mobileController_b_shape_confidence.js
index a27e0162..f1075840 100644
--- a/teleop/htm/static/JS/mobileController/mobileController_b_shape_confidence.js
+++ b/teleop/htm/static/JS/mobileController/mobileController_b_shape_confidence.js
@@ -2,126 +2,145 @@
* Handles toggle button interactions and manages WebSocket connections for real-time data updates.
*/
class ToggleButtonHandler {
- /**
- * Constructs a ToggleButtonHandler instance.
- * @param {string} buttonId The ID of the button element to be managed.
- */
- constructor(buttonId) {
- this.toggleButton = document.getElementById(buttonId);
- this.toggleButton.addEventListener('click', () => {
- this.handleButtonClick();
- });
- this.confidenceWS = {}; // Placeholder for WebSocket.
- this.autoReconnectInterval = 9000;
- this.initializeConfidenceWS();
- }
-
- /**
- * Initializes the WebSocket connection for real-time data updates and sets up event listeners.
- */
- initializeConfidenceWS() {
- let WSprotocol = document.location.protocol === 'https:' ? 'wss://' : 'ws://';
- this.currentURL = `${document.location.protocol}`
- let WSurl = `${WSprotocol}${document.location.hostname}:${document.location.port}/ws/switch_confidence`;
- this.confidenceWS.websocket = new WebSocket(WSurl);
-
- this.confidenceWS.websocket.onopen = (event) => {
- console.log('Confidence websocket connection opened');
- this.confidenceWS.isWebSocketOpen = true;
- };
-
- this.confidenceWS.websocket.onmessage = (event) => {
- console.log('Confidence WS:', event.data);
- this.updateButtonState(event.data);
-
- };
-
- this.confidenceWS.websocket.onerror = (error) => {
- // console.error('WebSocket Error:', error);
- };
-
- this.confidenceWS.websocket.onclose = (event) => {
- console.log('Confidence websocket connection closed');
- this.confidenceWS.isWebSocketOpen = false;
- // Automatically try to reconnect after a specified interval
- setTimeout(() => this.checkAndReconnectWebSocket(), this.autoReconnectInterval);
- };
- }
+ /**
+ * Constructs a ToggleButtonHandler instance.
+ * @param {string} buttonId The ID of the button element to be managed.
+ */
+ constructor(buttonId) {
+ this.toggleButton = document.getElementById(buttonId);
+ this.toggleButton.addEventListener('click', () => this.handleConfidenceToggleButtonClick());
+ this.confidenceWS = {}; // Placeholder for WebSocket.
+ this.autoReconnectInterval = 9000;
+ this.initializeConfidenceWS();
+ }
+
+ /**
+ * Initializes the WebSocket connection for real-time data updates and sets up event listeners.
+ */
+ initializeConfidenceWS() {
+ let WSprotocol = document.location.protocol === 'https:' ? 'wss://' : 'ws://';
+ this.currentURL = `${document.location.protocol}`;
+ let WSurl = `${WSprotocol}${document.location.hostname}:${document.location.port}/ws/switch_confidence`;
+ this.confidenceWS.websocket = new WebSocket(WSurl);
+
+ this.confidenceWS.websocket.onopen = (event) => {
+ console.log('Confidence websocket connection opened');
+ this.confidenceWS.isWebSocketOpen = true;
+ };
+
+ this.confidenceWS.websocket.onmessage = (event) => {
+ console.log('Confidence WS:', event.data);
+ this.updateButtonState(event.data);
+ };
+
+ this.confidenceWS.websocket.onerror = (error) => {
+ // console.error('WebSocket Error:', error);
+ };
+
+ this.confidenceWS.websocket.onclose = (event) => {
+ console.log('Confidence websocket connection closed');
+ this.confidenceWS.isWebSocketOpen = false;
+ // Automatically try to reconnect after a specified interval
+ setTimeout(() => this.checkAndReconnectWebSocket(), this.autoReconnectInterval);
+ };
+ }
+
+ /**
+ * Checks the WebSocket's current state and attempts to reconnect if it's closed.
+ */
+ checkAndReconnectWebSocket() {
+ if (!this.confidenceWS.websocket || this.confidenceWS.websocket.readyState === WebSocket.CLOSED) {
+ this.initializeConfidenceWS();
+ }
+ }
+ /**
+ * Handles button click events by sending appropriate commands based on the button's current state.
+ */
+ handleConfidenceToggleButtonClick() {
+ // Determine the command based on the opposite of the current button text
+ let currentText = this.toggleButton.innerText;
+ this.sendSwitchFollowingRequest(currentText);
+ }
- /**
- * Checks the WebSocket's current state and attempts to reconnect if it's closed.
- */
- checkAndReconnectWebSocket() {
- if (!this.confidenceWS.websocket || this.confidenceWS.websocket.readyState === WebSocket.CLOSED) {
- this.initializeConfidenceWS();
- }
- }
/**
- * Updates the button's appearance based on the received WebSocket message.
- * @param {string} message The message received from the WebSocket.
+ * Sends a command to the server via WebSocket.
+ * @param {string} command The command to be sent to the server.
*/
- updateButtonState(message) {
- if (message === 'loading') {
- this.toggleButton.innerHTML = 'Loading...';
- this.toggleButton.disabled = true;
- } else if (message.endsWith('.html')) {
- // Extract the filename from the message
- const filename = message.match(/[\w-]+\.html$/)[0];
-
- this.toggleButton.innerHTML = 'View Results'
- this.toggleButton.disabled = false;
- this.toggleButton.onclick = () => {
- window.location.href = `${this.currentURL}/overview_confidence/${filename}`;
- };
+ sendSwitchFollowingRequest(command) {
+ if (this.confidenceWS.websocket && this.confidenceWS.websocket.readyState === WebSocket.OPEN) {
+ this.confidenceWS.websocket.send(command);
+ this.toggleButtonAppearance(command)
+ } else {
+ console.error("Confidence websocket is not open. Command not sent. Attempting to reconnect...");
+ this.checkAndReconnectWebSocket();
}
}
- /**
- * Changes the button's appearance based on the current command.
- * @param {string} command The current text of the toggle button used to determine the new appearance.
- */
- toggleButtonAppearance(command) {
- this.toggleButton.innerText = command === "Start overview confidence" ? "Stop overview confidence" : "Start overview confidence";
- this.toggleButton.style.backgroundColor = command === "Start overview confidence" ? "#ff6347" : "#67b96a";
- }
-
- /**
- * Retrieves the value of a specified attribute of the toggle button.
- * @param {string} attributeName The name of the attribute to retrieve.
- * @returns {string} The value of the attribute.
- */
- getAttribute(attributeName) {
- return this.toggleButton.getAttribute(attributeName);
- }
-
- /**
- * Sets the value of a specified attribute of the toggle button.
- * @param {string} attributeName The name of the attribute to set.
- * @param {string} value The value to set for the attribute.
- */
- setAttribute(attributeName, value) {
- this.toggleButton.setAttribute(attributeName, value);
- }
-
- /**
- * Retrieves the style value of a specified property of the toggle button.
- * @param {string} property The CSS property name to retrieve.
- * @returns {string} The value of the CSS property.
- */
- getStyle(property) {
- return this.toggleButton.style[property];
- }
-
- /**
- * Sets the style of a specified property of the toggle button.
- * @param {string} property The CSS property name to set.
- * @param {string} value The value to set for the CSS property.
- */
- setStyle(property, value) {
- this.toggleButton.style[property] = value;
- }
+ /**
+ * Updates the button's appearance based on the received WebSocket message.
+ * @param {string} message The message received from the WebSocket.
+ */
+ updateButtonState(message) {
+ if (message === 'loading') {
+ this.toggleButton.innerHTML = 'Loading...';
+ this.toggleButton.disabled = true;
+ } else if (message.endsWith('.html')) {
+ // Extract the filename from the message
+ const filename = message.match(/[\w-]+\.html$/)[0];
+
+ this.toggleButton.innerHTML = 'View Results';
+ this.toggleButton.disabled = false;
+ this.toggleButton.onclick = () => {
+ window.location.href = `${this.currentURL}/overview_confidence/${filename}`;
+ };
+ }
+ }
+
+ /**
+ * Changes the button's appearance based on the current command.
+ * @param {string} command The current text of the toggle button used to determine the new appearance.
+ */
+ toggleButtonAppearance(command) {
+ this.toggleButton.innerText = command === 'Start overview confidence' ? 'Stop overview confidence' : 'Start overview confidence';
+ this.toggleButton.style.backgroundColor = command === 'Start overview confidence' ? '#ff6347' : '#67b96a';
+ }
+
+ /**
+ * Retrieves the value of a specified attribute of the toggle button.
+ * @param {string} attributeName The name of the attribute to retrieve.
+ * @returns {string} The value of the attribute.
+ */
+ getAttribute(attributeName) {
+ return this.toggleButton.getAttribute(attributeName);
+ }
+
+ /**
+ * Sets the value of a specified attribute of the toggle button.
+ * @param {string} attributeName The name of the attribute to set.
+ * @param {string} value The value to set for the attribute.
+ */
+ setAttribute(attributeName, value) {
+ this.toggleButton.setAttribute(attributeName, value);
+ }
+
+ /**
+ * Retrieves the style value of a specified property of the toggle button.
+ * @param {string} property The CSS property name to retrieve.
+ * @returns {string} The value of the CSS property.
+ */
+ getStyle(property) {
+ return this.toggleButton.style[property];
+ }
+
+ /**
+ * Sets the style of a specified property of the toggle button.
+ * @param {string} property The CSS property name to set.
+ * @param {string} value The value to set for the CSS property.
+ */
+ setStyle(property, value) {
+ this.toggleButton.style[property] = value;
+ }
}
-
export { ToggleButtonHandler };
diff --git a/teleop/htm/static/JS/mobileController/mobileController_b_shape_dot.js b/teleop/htm/static/JS/mobileController/mobileController_b_shape_dot.js
index e630c2c0..043d7c25 100644
--- a/teleop/htm/static/JS/mobileController/mobileController_b_shape_dot.js
+++ b/teleop/htm/static/JS/mobileController/mobileController_b_shape_dot.js
@@ -1,31 +1,31 @@
class Dot {
- constructor() {
- this.graphics = new PIXI.Graphics();
- this.drawDot(0, 0); // initial position
- }
+ constructor() {
+ this.graphics = new PIXI.Graphics();
+ this.drawDot(0, 0); // initial position
+ }
- drawDot(x, y) {
- this.graphics.clear();
- this.graphics.beginFill(0xffffff); // color for the dot
- this.graphics.drawCircle(x, y, 18); // The radius is 18 (was requested to have diameter of 10mm === 36px)
- this.graphics.endFill();
- }
+ drawDot(x, y) {
+ this.graphics.clear();
+ this.graphics.beginFill(0xffffff); // color for the dot
+ this.graphics.drawCircle(x, y, 18); // The radius is 18 (was requested to have diameter of 10mm === 36px)
+ this.graphics.endFill();
+ }
- setPosition(x, y) {
- this.drawDot(x, y);
- }
+ setPosition(x, y) {
+ this.drawDot(x, y);
+ }
- remove() {
- this.graphics.clear();
- }
+ remove() {
+ this.graphics.clear();
+ }
- hide() {
- this.graphics.alpha = 0;
- }
+ hide() {
+ this.graphics.alpha = 0;
+ }
- show() {
- this.graphics.alpha = 1;
- }
+ show() {
+ this.graphics.alpha = 1;
+ }
}
const cursorFollowingDot = new Dot();
diff --git a/teleop/htm/static/JS/mobileController/mobileController_b_shape_red_rectangle.js b/teleop/htm/static/JS/mobileController/mobileController_b_shape_red_rectangle.js
index cb0ff30b..a43cd063 100644
--- a/teleop/htm/static/JS/mobileController/mobileController_b_shape_red_rectangle.js
+++ b/teleop/htm/static/JS/mobileController/mobileController_b_shape_red_rectangle.js
@@ -1,71 +1,60 @@
-class Rectangle
-{
- constructor(position)
- {
- this.position = position; // The position of the rectangle in the screen => 'top' or 'bottom'
- this.height = (window.innerHeight/2); // The height of the rectangle. its height is half of the screen
- this.width = window.innerWidth; // The width of the rectangle. Its the entire width of the screen
- this.container = new PIXI.Container(); // The "canvas" onto which the rectangle will be drawn
- this.graphics = new PIXI.Graphics(); // The "painting" that will go on the canvas, the rectangle
-
- // Initiator methods of the class
- this.container.addChild(this.graphics);
- this.drawRectangle();
- this.drawText();
- }
-
- static headerTextStyle = new PIXI.TextStyle
- ({
- fontSize: 28,
- fill: 'white',
- align: 'center',
- });
-
- // Method that adds the text in the rectangle
- drawText()
- {
-
- // If we are drawing the top rectangle
- if (this.position === 'top')
- {
- this.textObj = new PIXI.Text('ɅɅɅ - STOP - ɅɅɅ', Rectangle.headerTextStyle); // Text that appears on the top rectangle
- this.textObj.anchor.set(0.5, 1); // Horizontally center and vertically bottom
- this.textObj.position.set(this.width/2, this.height); // Position at the center and bottom of the top rectangle
- }
-
- // If we are drawing the bottom rectangle
- else if (this.position === 'bottom')
- {
- this.textObj = new PIXI.Text('VVV - STOP - VVV', Rectangle.headerTextStyle); // Text that appears on the bottom rectangle
- this.textObj.anchor.set(0.5, 0); // Horizontally center and vertically top
- this.textObj.position.set(this.width/2, this.height); // Position at the center and top of the bottom rectangle
- }
-
- this.drawRectangle(); // Redraw to adjust position based on new text.
- this.graphics.addChild(this.textObj); // Add text to the rectangle
- }
-
- // Method that draws the rectangle on the local container
- drawRectangle()
- {
- this.graphics.clear();
- this.graphics.beginFill(0xff0000); // Red color
-
-
- // drawRect() arguments: X coords of the TL point, Y coords of the TL point, width, height
- if (this.position === 'top') // The top rectangle
- // The top left point of the top rectangle touches the top left corner of the screen => (0,0)
- this.graphics.drawRect(0, 0, this.width, this.height);
-
- else
- // The top left point of the top rectangle touches the left side of the screen, half of the screen down
- this.graphics.drawRect(0, this.height, this.width, this.height);
-
- this.graphics.endFill();
- }
+class Rectangle {
+ constructor(position) {
+ this.position = position; // The position of the rectangle in the screen => 'top' or 'bottom'
+ this.height = window.innerHeight / 2; // The height of the rectangle. its height is half of the screen
+ this.width = window.innerWidth; // The width of the rectangle. Its the entire width of the screen
+ this.container = new PIXI.Container(); // The "canvas" onto which the rectangle will be drawn
+ this.graphics = new PIXI.Graphics(); // The "painting" that will go on the canvas, the rectangle
+
+ // Initiator methods of the class
+ this.container.addChild(this.graphics);
+ this.drawText();
+ }
+
+ static headerTextStyle = new PIXI.TextStyle({
+ fontSize: 28,
+ fill: 'white',
+ align: 'center',
+ });
+
+ // Method that adds the text in the rectangle
+ drawText() {
+ // If we are drawing the top rectangle
+ if (this.position === 'top') {
+ this.textObj = new PIXI.Text('ɅɅɅ - STOP - ɅɅɅ', Rectangle.headerTextStyle); // Text that appears on the top rectangle
+ this.textObj.anchor.set(0.5, 1); // Horizontally center and vertically bottom
+ this.textObj.position.set(this.width / 2, this.height); // Position at the center and bottom of the top rectangle
+ }
+
+ // If we are drawing the bottom rectangle
+ else if (this.position === 'bottom') {
+ this.textObj = new PIXI.Text('VVV - STOP - VVV', Rectangle.headerTextStyle); // Text that appears on the bottom rectangle
+ this.textObj.anchor.set(0.5, 0); // Horizontally center and vertically top
+ this.textObj.position.set(this.width / 2, this.height); // Position at the center and top of the bottom rectangle
+ }
+
+ this.drawRectangle(); // Redraw to adjust position based on new text.
+ this.graphics.addChild(this.textObj); // Add text to the rectangle
+ }
+
+ // Method that draws the rectangle on the local container
+ drawRectangle() {
+ this.graphics.clear();
+ this.graphics.beginFill(0xff0000); // Red color
+
+ // drawRect() arguments: X coords of the TL point, Y coords of the TL point, width, height
+ if (this.position === 'top')
+ // The top rectangle
+ // The top left point of the top rectangle touches the top left corner of the screen => (0,0)
+ this.graphics.drawRect(0, 0, this.width, this.height);
+ // The top left point of the top rectangle touches the left side of the screen, half of the screen down
+ else this.graphics.drawRect(0, this.height, this.width, this.height);
+
+ this.graphics.endFill();
+ }
}
const topRectangle = new Rectangle('top');
const bottomRectangle = new Rectangle('bottom');
-export { topRectangle, bottomRectangle };
\ No newline at end of file
+export { topRectangle, bottomRectangle };
diff --git a/teleop/htm/static/JS/mobileController/mobileController_b_shape_triangle.js b/teleop/htm/static/JS/mobileController/mobileController_b_shape_triangle.js
index 07c82328..91f7aaa9 100644
--- a/teleop/htm/static/JS/mobileController/mobileController_b_shape_triangle.js
+++ b/teleop/htm/static/JS/mobileController/mobileController_b_shape_triangle.js
@@ -1,144 +1,140 @@
-
class Triangle {
- constructor(direction, text) {
- this.direction = direction;
- this.height = window.innerHeight / 4;
- this.baseWidth = 2 * (this.height / Math.sqrt(3));
- this.text = text;
- this.subText;
- this.container = new PIXI.Container();
- this.graphics = new PIXI.Graphics();
- this.currentSSID;
- this.container.addChild(this.graphics);
- this.updateDimensions();
- this.drawTriangle();
- if (text) {
- this.drawText(text);
- }
-
- if (this.direction === 'up') {
- if (!this.text) {
- this.getSSID();
- }
- }
- }
-
-
- async getSSID() {
- try {
- const response = await fetch('/run_get_SSID');
- this.currentSSID = await response.text();
- this.drawText(this.currentSSID); // Call function to redraw the text after it has being fetched
- } catch (error) {
- console.error("Error fetching SSID for current robot:", error);
- }
- }
-
- changeText(newText, fontSize = undefined) {
- if (newText === "controlError") {
- newText = "Control Lost";
- this.subText = "Refresh the Page";
- } else if (newText === "connectionError") {
- newText = "Connection Lost";
- this.subText = "Please reconnect";
- }
- if (this.textObj) {
- this.graphics.removeChild(this.textObj);
- }
- this.text = newText;
- this.drawText(newText, fontSize);
- }
-
-
- drawText(newText, fontSize) {
- const headerTextStyle = new PIXI.TextStyle({
- fontSize: fontSize || 28, // Use provided fontSize or default to 28
- fill: 'white',
- align: 'center',
- });
-
- this.textObj = new PIXI.Text(newText, headerTextStyle);
-
- // Remove previous subTextObj if it exists
- if (this.subTextObj) {
- this.graphics.removeChild(this.subTextObj);
- }
-
- // Check and draw subText
- if (this.direction === 'up' && this.subText) {
- const subTextStyle = new PIXI.TextStyle({
- fontSize: (fontSize ? fontSize * 0.5 : 15), // Example: smaller font size for subText
- fill: 'white',
- align: 'center',
- });
- this.subTextObj = new PIXI.Text(this.subText, subTextStyle);
- this.subTextObj.anchor.set(0.5, 0); // Horizontally center and vertically top
- this.graphics.addChild(this.subTextObj);
- }
-
- if (this.direction === 'up') {
- this.textObj.anchor.set(0.5, 0); // Horizontally center and vertically top
- } else {
- this.textObj.anchor.set(0.5, 1); // Horizontally center and vertically bottom
- }
- this.graphics.addChild(this.textObj);
- this.drawTriangle(); // Redraw to adjust position based on new text.
- }
-
- /**
- * Limit the width to be only a maximum of 600px (for mobile screens)
- */
- updateDimensions() {
- if (this.baseWidth > 600) {
- this.baseWidth = 600;
- this.height = (this.baseWidth * Math.sqrt(3)) / 2;
- }
- }
-
-
- drawTriangle(yOffset = 0, color = 0x000000) {
- this.graphics.clear();
- this.graphics.beginFill(color); // Use this.color or default to black
- const midScreen = window.innerHeight / 2;
- const yOffsetAdjustment = this.direction === 'up' ? -5 : 5;// Tip between the two triangle
-
- if (this.direction === 'up') {
- this.vertices = [
- [(window.innerWidth - this.baseWidth) / 2, yOffset + midScreen - this.height + yOffsetAdjustment],
- [(window.innerWidth + this.baseWidth) / 2, yOffset + midScreen - this.height + yOffsetAdjustment],
- [window.innerWidth / 2, yOffset + midScreen + yOffsetAdjustment],
- ];
- } else {
- this.vertices = [
- [(window.innerWidth - this.baseWidth) / 2, yOffset + midScreen + this.height + yOffsetAdjustment],
- [(window.innerWidth + this.baseWidth) / 2, yOffset + midScreen + this.height + yOffsetAdjustment],
- [window.innerWidth / 2, yOffset + midScreen + yOffsetAdjustment],
- ];
- }
- if (this.textObj) {
- this.alignText(yOffset, midScreen, yOffsetAdjustment)
- }
- this.graphics.drawPolygon(this.vertices.flat());
- this.graphics.endFill();
- }
-
- alignText(yOffset, midScreen, yOffsetAdjustment) {
- if (this.direction === 'up') {
- // Positioning the text below the base of the upper triangle
- this.textObj.position.set(window.innerWidth / 2, yOffset + midScreen - this.height + yOffsetAdjustment + 5);
-
- // Positioning the subText 10px below the header text
- if (this.subTextObj) {
- this.subTextObj.position.set(window.innerWidth / 2, this.textObj.position.y + this.textObj.height + 5);
- }
- } else {
- // Positioning the text above the base of the lower triangle
- this.textObj.position.set(window.innerWidth / 2, yOffset + midScreen + this.height);
- }
- }
+ constructor(direction, text) {
+ this.direction = direction;
+ this.height = window.innerHeight / 4;
+ this.baseWidth = 2 * (this.height / Math.sqrt(3));
+ this.text = text;
+ this.subText;
+ this.container = new PIXI.Container();
+ this.graphics = new PIXI.Graphics();
+ this.currentSSID;
+ this.container.addChild(this.graphics);
+ this.updateDimensions();
+ this.drawTriangle();
+ if (text) {
+ this.drawText(text);
+ }
+
+ if (this.direction === 'up') {
+ if (!this.text) {
+ this.getSSID();
+ }
+ }
+ }
+
+ async getSSID() {
+ try {
+ const response = await fetch('/run_get_SSID');
+ this.currentSSID = await response.text();
+ this.drawText(this.currentSSID); // Call function to redraw the text after it has being fetched
+ } catch (error) {
+ console.error('Error fetching SSID for current robot:', error);
+ }
+ }
+
+ changeText(newText, fontSize = undefined) {
+ if (newText === 'controlError') {
+ newText = 'Control Lost';
+ this.subText = 'Refresh the Page';
+ } else if (newText === 'connectionError') {
+ newText = 'Connection Lost';
+ this.subText = 'Please reconnect';
+ }
+ if (this.textObj) {
+ this.graphics.removeChild(this.textObj);
+ }
+ this.text = newText;
+ this.drawText(newText, fontSize);
+ }
+
+ drawText(newText, fontSize) {
+ const headerTextStyle = new PIXI.TextStyle({
+ fontSize: fontSize || 28, // Use provided fontSize or default to 28
+ fill: 'white',
+ align: 'center',
+ });
+
+ this.textObj = new PIXI.Text(newText, headerTextStyle);
+
+ // Remove previous subTextObj if it exists
+ if (this.subTextObj) {
+ this.graphics.removeChild(this.subTextObj);
+ }
+
+ // Check and draw subText
+ if (this.direction === 'up' && this.subText) {
+ const subTextStyle = new PIXI.TextStyle({
+ fontSize: fontSize ? fontSize * 0.5 : 15, // Example: smaller font size for subText
+ fill: 'white',
+ align: 'center',
+ });
+ this.subTextObj = new PIXI.Text(this.subText, subTextStyle);
+ this.subTextObj.anchor.set(0.5, 0); // Horizontally center and vertically top
+ this.graphics.addChild(this.subTextObj);
+ }
+
+ if (this.direction === 'up') {
+ this.textObj.anchor.set(0.5, 0); // Horizontally center and vertically top
+ } else {
+ this.textObj.anchor.set(0.5, 1); // Horizontally center and vertically bottom
+ }
+ this.graphics.addChild(this.textObj);
+ this.drawTriangle(); // Redraw to adjust position based on new text.
+ }
+
+ /**
+ * Limit the width to be only a maximum of 600px (for mobile screens)
+ */
+ updateDimensions() {
+ if (this.baseWidth > 600) {
+ this.baseWidth = 600;
+ this.height = (this.baseWidth * Math.sqrt(3)) / 2;
+ }
+ }
+
+ drawTriangle(yOffset = 0, color = 0x000000) {
+ this.graphics.clear();
+ this.graphics.beginFill(color); // Use this.color or default to black
+ const midScreen = window.innerHeight / 2;
+ const yOffsetAdjustment = this.direction === 'up' ? -5 : 5; // Tip between the two triangle
+
+ if (this.direction === 'up') {
+ this.vertices = [
+ [(window.innerWidth - this.baseWidth) / 2, yOffset + midScreen - this.height + yOffsetAdjustment],
+ [(window.innerWidth + this.baseWidth) / 2, yOffset + midScreen - this.height + yOffsetAdjustment],
+ [window.innerWidth / 2, yOffset + midScreen + yOffsetAdjustment],
+ ];
+ } else {
+ this.vertices = [
+ [(window.innerWidth - this.baseWidth) / 2, yOffset + midScreen + this.height + yOffsetAdjustment],
+ [(window.innerWidth + this.baseWidth) / 2, yOffset + midScreen + this.height + yOffsetAdjustment],
+ [window.innerWidth / 2, yOffset + midScreen + yOffsetAdjustment],
+ ];
+ }
+ if (this.textObj) {
+ this.alignText(yOffset, midScreen, yOffsetAdjustment);
+ }
+ this.graphics.drawPolygon(this.vertices.flat());
+ this.graphics.endFill();
+ }
+
+ alignText(yOffset, midScreen, yOffsetAdjustment) {
+ if (this.direction === 'up') {
+ // Positioning the text below the base of the upper triangle
+ this.textObj.position.set(window.innerWidth / 2, yOffset + midScreen - this.height + yOffsetAdjustment + 5);
+
+ // Positioning the subText 10px below the header text
+ if (this.subTextObj) {
+ this.subTextObj.position.set(window.innerWidth / 2, this.textObj.position.y + this.textObj.height + 5);
+ }
+ } else {
+ // Positioning the text above the base of the lower triangle
+ this.textObj.position.set(window.innerWidth / 2, yOffset + midScreen + this.height);
+ }
+ }
}
const topTriangle = new Triangle('up');
const bottomTriangle = new Triangle('down', 'Backwards');
-export { topTriangle, bottomTriangle };
\ No newline at end of file
+export { topTriangle, bottomTriangle };
diff --git a/teleop/htm/static/JS/mobileController/mobileController_c_logic.js b/teleop/htm/static/JS/mobileController/mobileController_c_logic.js
index cd583a7b..809c601e 100644
--- a/teleop/htm/static/JS/mobileController/mobileController_c_logic.js
+++ b/teleop/htm/static/JS/mobileController/mobileController_c_logic.js
@@ -1,44 +1,43 @@
-
-import { cursorFollowingDot } from "./mobileController_b_shape_dot.js";
-import { bottomTriangle, topTriangle } from "./mobileController_b_shape_triangle.js";
-
+import { followingButtonHandler } from './mobileController_b_following.js';
+import { cursorFollowingDot } from './mobileController_b_shape_dot.js';
+import { bottomTriangle, topTriangle } from './mobileController_b_shape_triangle.js';
import { drawBottomTriangle_TopRectangle, drawTopTriangle_BottomRectangle, redraw } from './mobileController_d_pixi.js';
import CTRL_STAT from './mobileController_z_state.js';
function initializeWS() {
- let WSprotocol = document.location.protocol === 'https:' ? 'wss://' : 'ws://';
- let WSurl = `${WSprotocol}${document.location.hostname}:${document.location.port}/ws/send_mobile_controller_commands`;
- CTRL_STAT.websocket = new WebSocket(WSurl);
-
- CTRL_STAT.websocket.onopen = function (event) {
- console.log('Mobile controller (WS) connection opened');
- addKeyToSentCommand("button_b", 1)
- CTRL_STAT.stateErrors = ""
- CTRL_STAT.isWebSocketOpen = true;
- };
-
- // Check the respond from the endpoint. If the user is operator or viewer
- // if it is a viewer, then refresh
- CTRL_STAT.websocket.onmessage = function (event) {
- let parsedData = JSON.parse(event.data); // The received data is in string, so I need to convert to JSON
- if (parsedData["control"] == "operator") {
- //Place holder until implementation with multi segment is over
- } else if (parsedData["control"] == "viewer") {
- CTRL_STAT.stateErrors = "controlError"
- redraw(undefined, true, true, false);
- }
- };
-
- CTRL_STAT.websocket.onerror = function (error) {
- console.log('WebSocket Error:', error);
- };
-
- CTRL_STAT.websocket.onclose = function (event) {
- console.log('Mobile controller (WS) connection closed');
- CTRL_STAT.stateErrors = "connectionError"
- redraw(undefined, true, true, false);
- CTRL_STAT.isWebSocketOpen = false; // Reset the flag when WebSocket is closed
- };
+ let WSprotocol = document.location.protocol === 'https:' ? 'wss://' : 'ws://';
+ let WSurl = `${WSprotocol}${document.location.hostname}:${document.location.port}/ws/send_mobile_controller_commands`;
+ CTRL_STAT.websocket = new WebSocket(WSurl);
+
+ CTRL_STAT.websocket.onopen = function (event) {
+ console.log('Mobile controller (WS) connection opened');
+ addKeyToSentCommand('button_b', 1);
+ CTRL_STAT.stateErrors = '';
+ CTRL_STAT.isWebSocketOpen = true;
+ };
+
+ // Check the respond from the endpoint. If the user is operator or viewer
+ // if it is a viewer, then refresh
+ CTRL_STAT.websocket.onmessage = function (event) {
+ let parsedData = JSON.parse(event.data); // The received data is in string, so I need to convert to JSON
+ if (parsedData['control'] == 'operator') {
+ //Place holder until implementation with multi segment is over
+ } else if (parsedData['control'] == 'viewer') {
+ CTRL_STAT.stateErrors = 'controlError';
+ redraw(undefined, true, true, false);
+ }
+ };
+
+ CTRL_STAT.websocket.onerror = function (error) {
+ console.log('WebSocket Error:', error);
+ };
+
+ CTRL_STAT.websocket.onclose = function (event) {
+ console.log('Mobile controller (WS) connection closed');
+ CTRL_STAT.stateErrors = 'connectionError';
+ redraw(undefined, true, true, false);
+ CTRL_STAT.isWebSocketOpen = false; // Reset the flag when WebSocket is closed
+ };
}
/**
@@ -51,28 +50,28 @@ function initializeWS() {
* @param {number} by Triangle's 2nd edge y-coord
* @param {number} cx Triangle's 3rd edge x-coord
* @param {number} cy Triangle's 3rd edge y-coord
- * @returns
+ * @returns
*/
function pointInsideTriangle(px, py, ax, ay, bx, by, cx, cy) {
- // Compute vectors
- const v0 = [cx - ax, cy - ay];
- const v1 = [bx - ax, by - ay];
- const v2 = [px - ax, py - ay];
-
- // Compute dot products
- const dot00 = v0[0] * v0[0] + v0[1] * v0[1];
- const dot01 = v0[0] * v1[0] + v0[1] * v1[1];
- const dot02 = v0[0] * v2[0] + v0[1] * v2[1];
- const dot11 = v1[0] * v1[0] + v1[1] * v1[1];
- const dot12 = v1[0] * v2[0] + v1[1] * v2[1];
-
- // Compute barycentric coordinates
- const invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
- const u = (dot11 * dot02 - dot01 * dot12) * invDenom;
- const v = (dot00 * dot12 - dot01 * dot02) * invDenom;
-
- // Check if the point is inside the triangle
- return u >= 0 && v >= 0 && u + v < 1;
+ // Compute vectors
+ const v0 = [cx - ax, cy - ay];
+ const v1 = [bx - ax, by - ay];
+ const v2 = [px - ax, py - ay];
+
+ // Compute dot products
+ const dot00 = v0[0] * v0[0] + v0[1] * v0[1];
+ const dot01 = v0[0] * v1[0] + v0[1] * v1[1];
+ const dot02 = v0[0] * v2[0] + v0[1] * v2[1];
+ const dot11 = v1[0] * v1[0] + v1[1] * v1[1];
+ const dot12 = v1[0] * v2[0] + v1[1] * v2[1];
+
+ // Compute barycentric coordinates
+ const invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
+ const u = (dot11 * dot02 - dot01 * dot12) * invDenom;
+ const v = (dot00 * dot12 - dot01 * dot02) * invDenom;
+
+ // Check if the point is inside the triangle
+ return u >= 0 && v >= 0 && u + v < 1;
}
/**
@@ -81,44 +80,41 @@ function pointInsideTriangle(px, py, ax, ay, bx, by, cx, cy) {
* @param {number} user_touch_X - X position of the user's touch input.
*/
function deltaCoordinatesFromTip(user_touch_X) {
- let relativeX = user_touch_X - window.innerWidth / 2;
- return relativeX
+ let relativeX = user_touch_X - window.innerWidth / 2;
+ return relativeX;
}
-
function SetStatistics(user_touch_X, user_touch_Y, y, getInferenceState) {
- let shapeHeight = window.innerHeight / 4; //It is the same value as in updateDimensions()=> this.height
- const isTopTriangle = CTRL_STAT.selectedTriangle === 'top'; // Checking if the top triangle is in use
- const isTouchBelowCenter = user_touch_Y >= window.innerHeight / 2; // Checking if the finger of the user is below the center line of the screen
-
-
- // Stopping the robot urgently depending on where the finger of the user is. We stop in these cases:
- // top triangle in use AND finger below the center
- // OR
- // bottom triangle in use AND finger above the center
- // In this case will immediately stop
- if (isTopTriangle === isTouchBelowCenter)
- // Removing the throttle key in the JSON, since the robot will only forward commands if they have the 'throttle' key inside
- CTRL_STAT.throttleSteeringJson = {};
-
- // In any other case, we produce commands normally
- else
- CTRL_STAT.throttleSteeringJson = {
- throttle: -(y).toFixed(3),
- steering: Number((user_touch_X / (shapeHeight / Math.sqrt(3))).toFixed(3)),
- mobileInferenceState: getInferenceState,
- };
+ let shapeHeight = window.innerHeight / 4; //It is the same value as in updateDimensions()=> this.height
+ const isTopTriangle = CTRL_STAT.selectedTriangle === 'top'; // Checking if the top triangle is in use
+ const isTouchBelowCenter = user_touch_Y >= window.innerHeight / 2; // Checking if the finger of the user is below the center line of the screen
+
+ // Stopping the robot urgently depending on where the finger of the user is. We stop in these cases:
+ // top triangle in use AND finger below the center
+ // OR
+ // bottom triangle in use AND finger above the center
+ // In this case will immediately stop
+ if (isTopTriangle === isTouchBelowCenter)
+ // Removing the throttle key in the JSON, since the robot will only forward commands if they have the 'throttle' key inside
+ CTRL_STAT.throttleSteeringJson = {};
+ // In any other case, we produce commands normally
+ else
+ CTRL_STAT.throttleSteeringJson = {
+ throttle: -y.toFixed(3),
+ steering: Number((user_touch_X / (shapeHeight / Math.sqrt(3))).toFixed(3)),
+ mobileInferenceState: getInferenceState,
+ };
}
// Save dead zone width to local storage
function saveDeadZoneWidth(value) {
- localStorage.setItem('deadZoneWidth', value);
+ localStorage.setItem('deadZoneWidth', value);
}
// Retrieve dead zone width from local storage
function getSavedDeadZoneWidth() {
- // If there's a saved value in local storage, use it; otherwise default to 0.1
- return localStorage.getItem('deadZoneWidth') || '0.1';
+ // If there's a saved value in local storage, use it; otherwise default to 0.1
+ return localStorage.getItem('deadZoneWidth') || '0.1';
}
/**
@@ -128,56 +124,56 @@ function getSavedDeadZoneWidth() {
* @param {*} getInferenceState - Function to get the current inference state.
*/
function handleDotMove(touchX, touchY, getInferenceState) {
- // Determine the triangle and its vertical boundaries based on the selection.
- const isTopTriangle = CTRL_STAT.selectedTriangle === 'top';
- const triangle = isTopTriangle ? topTriangle : bottomTriangle;
- const midScreen = window.innerHeight / 2;
-
- // Calculate minY and maxY based on the mode.
- let minY, maxY;
- if (getInferenceState === "auto") {
- minY = isTopTriangle ? midScreen - triangle.height : midScreen;
- maxY = isTopTriangle ? midScreen : midScreen + triangle.height;
- } else {
- minY = isTopTriangle ? CTRL_STAT.midScreen - triangle.height : CTRL_STAT.midScreen;
- maxY = isTopTriangle ? CTRL_STAT.midScreen : CTRL_STAT.midScreen + triangle.height;
- }
-
- // Constrain the Y position within the triangle's boundaries.
- let y = Math.max(minY, Math.min(touchY, maxY));
-
- // Calculate the relative Y position within the triangle.
- // This is initialized here to ensure it has a value in all code paths.
- let relativeY = (y - (getInferenceState === "auto" ? midScreen : CTRL_STAT.midScreen)) / triangle.height;
-
- let deadZoneSlider = document.getElementById('deadZoneWidth');
- let savedDeadZoneWidth = getSavedDeadZoneWidth();
- deadZoneSlider.value = savedDeadZoneWidth; // Set the slider to the saved value
- let deadZoneWidth = window.innerWidth * parseFloat(savedDeadZoneWidth);
-
- let deadZoneMinX = (window.innerWidth / 2) - (deadZoneWidth / 2);
- let deadZoneMaxX = (window.innerWidth / 2) + (deadZoneWidth / 2);
- let inDeadZone = touchX >= deadZoneMinX && touchX <= deadZoneMaxX;
-
- // Modify the logic to handle the X position considering the dead zone
- let xOfDot;
-
- if (inDeadZone) {
- xOfDot = window.innerWidth / 2;
- relativeY = (y - CTRL_STAT.midScreen) / triangle.height; // Default value when in dead zone, adjust as necessary
- } else {
- let maxXDeviation = Math.abs(relativeY) * (triangle.baseWidth / 2);
- xOfDot = Math.max(Math.min(touchX, window.innerWidth / 2 + maxXDeviation), window.innerWidth / 2 - maxXDeviation);
- }
-
- // Update the dot's position.
- cursorFollowingDot.setPosition(xOfDot, y);
- if (inDeadZone) {
- SetStatistics(0, y, relativeY, getInferenceState);
- } else if (getInferenceState !== "auto") {
- let relative_x = deltaCoordinatesFromTip(touchX);
- SetStatistics(relative_x, touchY, relativeY, getInferenceState);
- }
+ // Determine the triangle and its vertical boundaries based on the selection.
+ const isTopTriangle = CTRL_STAT.selectedTriangle === 'top';
+ const triangle = isTopTriangle ? topTriangle : bottomTriangle;
+ const midScreen = window.innerHeight / 2;
+
+ // Calculate minY and maxY based on the mode.
+ let minY, maxY;
+ if (getInferenceState === 'auto') {
+ minY = isTopTriangle ? midScreen - triangle.height : midScreen;
+ maxY = isTopTriangle ? midScreen : midScreen + triangle.height;
+ } else {
+ minY = isTopTriangle ? CTRL_STAT.midScreen - triangle.height : CTRL_STAT.midScreen;
+ maxY = isTopTriangle ? CTRL_STAT.midScreen : CTRL_STAT.midScreen + triangle.height;
+ }
+
+ // Constrain the Y position within the triangle's boundaries.
+ let y = Math.max(minY, Math.min(touchY, maxY));
+
+ // Calculate the relative Y position within the triangle.
+ // This is initialized here to ensure it has a value in all code paths.
+ let relativeY = (y - (getInferenceState === 'auto' ? midScreen : CTRL_STAT.midScreen)) / triangle.height;
+
+ let deadZoneSlider = document.getElementById('deadZoneWidth');
+ let savedDeadZoneWidth = getSavedDeadZoneWidth();
+ deadZoneSlider.value = savedDeadZoneWidth; // Set the slider to the saved value
+ let deadZoneWidth = window.innerWidth * parseFloat(savedDeadZoneWidth);
+
+ let deadZoneMinX = window.innerWidth / 2 - deadZoneWidth / 2;
+ let deadZoneMaxX = window.innerWidth / 2 + deadZoneWidth / 2;
+ let inDeadZone = touchX >= deadZoneMinX && touchX <= deadZoneMaxX;
+
+ // Modify the logic to handle the X position considering the dead zone
+ let xOfDot;
+
+ if (inDeadZone) {
+ xOfDot = window.innerWidth / 2;
+ relativeY = (y - CTRL_STAT.midScreen) / triangle.height; // Default value when in dead zone, adjust as necessary
+ } else {
+ let maxXDeviation = Math.abs(relativeY) * (triangle.baseWidth / 2);
+ xOfDot = Math.max(Math.min(touchX, window.innerWidth / 2 + maxXDeviation), window.innerWidth / 2 - maxXDeviation);
+ }
+
+ // Update the dot's position.
+ cursorFollowingDot.setPosition(xOfDot, y);
+ if (inDeadZone) {
+ SetStatistics(0, y, relativeY, getInferenceState);
+ } else if (getInferenceState !== 'auto') {
+ let relative_x = deltaCoordinatesFromTip(touchX);
+ SetStatistics(relative_x, touchY, relativeY, getInferenceState);
+ }
}
/**
@@ -187,101 +183,90 @@ function handleDotMove(touchX, touchY, getInferenceState) {
* @returns If the touch was in any of the two triangles, or even out
*/
function detectTriangle(x, y) {
- //Lots of spread syntax/ destructuring for the values of vertices in triangles
- if (pointInsideTriangle(x, y, ...topTriangle.vertices[0], ...topTriangle.vertices[1], ...topTriangle.vertices[2])) {
- CTRL_STAT.detectedTriangle = 'top';
- } else if (pointInsideTriangle(x, y, ...bottomTriangle.vertices[0], ...bottomTriangle.vertices[1], ...bottomTriangle.vertices[2])) {
- CTRL_STAT.detectedTriangle = 'bottom';
- } else {
- CTRL_STAT.detectedTriangle = 'none';
- }
- CTRL_STAT.selectedTriangle = CTRL_STAT.detectedTriangle;
+ //Lots of spread syntax/ destructuring for the values of vertices in triangles
+ if (pointInsideTriangle(x, y, ...topTriangle.vertices[0], ...topTriangle.vertices[1], ...topTriangle.vertices[2])) {
+ CTRL_STAT.detectedTriangle = 'top';
+ } else if (pointInsideTriangle(x, y, ...bottomTriangle.vertices[0], ...bottomTriangle.vertices[1], ...bottomTriangle.vertices[2])) {
+ CTRL_STAT.detectedTriangle = 'bottom';
+ } else {
+ CTRL_STAT.detectedTriangle = 'none';
+ }
+ CTRL_STAT.selectedTriangle = CTRL_STAT.detectedTriangle;
}
/**
* limit the triangles not to go outside the borders of the screen
- * @param {number} y Y-coord where the user's input is
+ * @param {number} y Y-coord where the user's input is
*/
function handleTriangleMove(y, inferenceToggleButton) {
- const midScreen = window.innerHeight / 2;
- let yOffset = y - midScreen;
-
- const maxOffset = midScreen + topTriangle.height; // Maximum offset for the top triangle
- const minOffset = midScreen + bottomTriangle.height; // Minimum offset for the bottom triangle
-
- // Clamping the yOffset value to stop the triangles from crossing the screen's border
- if (yOffset > 0) {
- yOffset = Math.min(yOffset, minOffset - midScreen);
- } else {
- yOffset = Math.max(yOffset, -(maxOffset - midScreen));
- }
-
- let INFState = inferenceToggleButton.getInferenceState
- if (INFState == "auto") {
- inferenceToggleButton.handleSpeedControl(CTRL_STAT.selectedTriangle)
- } else if (INFState == "train") {
- redraw(yOffset, true, false, true);
- } else if (CTRL_STAT.detectedTriangle === 'top' && INFState != "true") {
- document.getElementById('toggle_button_container').style.display = 'none';
- drawTopTriangle_BottomRectangle(yOffset);
- }
- else if (CTRL_STAT.detectedTriangle === 'bottom' && INFState != "true") {
- document.getElementById('toggle_button_container').style.display = 'none';
- drawBottomTriangle_TopRectangle(yOffset);
- }
-
-
- else if (CTRL_STAT.detectedTriangle === 'top') {
- document.getElementById('toggle_button_container').style.display = 'none';
- drawTopTriangle_BottomRectangle(yOffset);
- }
- else if (CTRL_STAT.detectedTriangle === 'bottom') {
- document.getElementById('toggle_button_container').style.display = 'none';
- drawBottomTriangle_TopRectangle(yOffset);
- }
+ const midScreen = window.innerHeight / 2;
+ let yOffset = y - midScreen;
+
+ const maxOffset = midScreen + topTriangle.height; // Maximum offset for the top triangle
+ const minOffset = midScreen + bottomTriangle.height; // Minimum offset for the bottom triangle
+
+ // Clamping the yOffset value to stop the triangles from crossing the screen's border
+ if (yOffset > 0) {
+ yOffset = Math.min(yOffset, minOffset - midScreen);
+ } else {
+ yOffset = Math.max(yOffset, -(maxOffset - midScreen));
+ }
+
+ let INFState = inferenceToggleButton.getInferenceState;
+ // let FOLState =
+ if (INFState == 'auto') {
+ inferenceToggleButton.handleSpeedControl(CTRL_STAT.selectedTriangle);
+ } else if (INFState == 'train') {
+ redraw(yOffset, true, false, true);
+ } else if (CTRL_STAT.detectedTriangle === 'top' && INFState != 'true') {
+ document.getElementById('toggle_button_container').style.display = 'none';
+ drawTopTriangle_BottomRectangle(yOffset);
+ } else if (CTRL_STAT.detectedTriangle === 'bottom' && INFState != 'true') {
+ document.getElementById('toggle_button_container').style.display = 'none';
+ drawBottomTriangle_TopRectangle(yOffset);
+ } else if (CTRL_STAT.detectedTriangle === 'top') {
+ document.getElementById('toggle_button_container').style.display = 'none';
+ drawTopTriangle_BottomRectangle(yOffset);
+ }
}
/**
* Function to add a temporary key-value pair to the sent command through mobile controller socket
- * @param {string} key
- * @param {string} value
+ * @param {string} key
+ * @param {string} value
*/
function addKeyToSentCommand(key, value) {
- CTRL_STAT.throttleSteeringJson[key + "_temp"] = value;
-
+ CTRL_STAT.throttleSteeringJson[key + '_temp'] = value;
}
function sendJSONCommand() {
- if (CTRL_STAT.websocket && CTRL_STAT.websocket.readyState === WebSocket.OPEN) {
- // Create a copy of the data to send, removing '_temp' from temporary keys
- const dataToSend = {};
- for (const key in CTRL_STAT.throttleSteeringJson) {
- if (key.endsWith('_temp')) {
- const originalKey = key.slice(0, -5); // Remove last 5 characters ('_temp')
- dataToSend[originalKey] = CTRL_STAT.throttleSteeringJson[key];
- } else {
- dataToSend[key] = CTRL_STAT.throttleSteeringJson[key];
- }
- }
-
- CTRL_STAT.websocket.send(JSON.stringify(dataToSend));
- CTRL_STAT.isWebSocketOpen = true;
-
- Object.keys(CTRL_STAT.throttleSteeringJson).forEach(key => {
- if (key.endsWith('_temp')) {
- delete CTRL_STAT.throttleSteeringJson[key];
- }
- });
-
- } else {
- if (CTRL_STAT.isWebSocketOpen) {
- console.error('WebSocket is not open. Unable to send data.');
- CTRL_STAT.isWebSocketOpen = false;
- }
- }
-
- setTimeout(sendJSONCommand, 100);
+ if (CTRL_STAT.websocket && CTRL_STAT.websocket.readyState === WebSocket.OPEN) {
+ // Create a copy of the data to send, removing '_temp' from temporary keys
+ const dataToSend = {};
+ for (const key in CTRL_STAT.throttleSteeringJson) {
+ if (key.endsWith('_temp')) {
+ const originalKey = key.slice(0, -5); // Remove last 5 characters ('_temp')
+ dataToSend[originalKey] = CTRL_STAT.throttleSteeringJson[key];
+ } else {
+ dataToSend[key] = CTRL_STAT.throttleSteeringJson[key];
+ }
+ }
+ CTRL_STAT.websocket.send(JSON.stringify(dataToSend));
+ CTRL_STAT.isWebSocketOpen = true;
+
+ Object.keys(CTRL_STAT.throttleSteeringJson).forEach((key) => {
+ if (key.endsWith('_temp')) {
+ delete CTRL_STAT.throttleSteeringJson[key];
+ }
+ });
+ } else {
+ if (CTRL_STAT.isWebSocketOpen) {
+ console.error('WebSocket is not open. Unable to send data.');
+ CTRL_STAT.isWebSocketOpen = false;
+ }
+ }
+
+ setTimeout(sendJSONCommand, 100);
}
export { addKeyToSentCommand, deltaCoordinatesFromTip, detectTriangle, getSavedDeadZoneWidth, handleDotMove, handleTriangleMove, initializeWS, pointInsideTriangle, saveDeadZoneWidth, sendJSONCommand };
-
diff --git a/teleop/htm/static/JS/mobileController/mobileController_d_pixi.js b/teleop/htm/static/JS/mobileController/mobileController_d_pixi.js
index 99474e66..9dd58ee5 100644
--- a/teleop/htm/static/JS/mobileController/mobileController_d_pixi.js
+++ b/teleop/htm/static/JS/mobileController/mobileController_d_pixi.js
@@ -1,14 +1,14 @@
-import { cursorFollowingDot } from "./mobileController_b_shape_dot.js";
-import { topTriangle, bottomTriangle } from "./mobileController_b_shape_triangle.js"
-import { topRectangle, bottomRectangle } from "./mobileController_b_shape_red_rectangle.js"
-import { MotorDataInput } from "./mobileController_e_scale_offset_input.js";
+import { cursorFollowingDot } from './mobileController_b_shape_dot.js';
+import { topTriangle, bottomTriangle } from './mobileController_b_shape_triangle.js';
+import { topRectangle, bottomRectangle } from './mobileController_b_shape_red_rectangle.js';
+import { MotorDataInput } from './mobileController_e_scale_offset_input.js';
import CTRL_STAT from './mobileController_z_state.js';
const app = new PIXI.Application({
- width: window.innerWidth,
- height: window.innerHeight - 20,
- backgroundColor: 0xffffff,
- sharedTicker: true,
+ width: window.innerWidth,
+ height: window.innerHeight - 20,
+ backgroundColor: 0xffffff,
+ sharedTicker: true,
});
// Add a 10px margin to the top and bottom of the canvas
@@ -20,82 +20,77 @@ document.body.appendChild(app.view);
app.stage.addChild(topTriangle.graphics);
app.stage.addChild(bottomTriangle.graphics);
-
function removeTriangles() {
- // Check if the top triangle's graphics are currently a child of the stage
- if (app.stage.children.includes(topTriangle.graphics)) {
- app.stage.removeChild(topTriangle.graphics);
- }
-
- // Check if the bottom triangle's graphics are currently a child of the stage
- if (app.stage.children.includes(bottomTriangle.graphics)) {
- app.stage.removeChild(bottomTriangle.graphics);
- }
+ // Check if the top triangle's graphics are currently a child of the stage
+ if (app.stage.children.includes(topTriangle.graphics)) {
+ app.stage.removeChild(topTriangle.graphics);
+ }
+
+ // Check if the bottom triangle's graphics are currently a child of the stage
+ if (app.stage.children.includes(bottomTriangle.graphics)) {
+ app.stage.removeChild(bottomTriangle.graphics);
+ }
}
-function changeTrianglesColor(color = "0x000000") {
- topTriangle.drawTriangle(undefined, color);
- bottomTriangle.drawTriangle(undefined, color);
+function changeTrianglesColor(color = '0x000000') {
+ topTriangle.drawTriangle(undefined, color);
+ bottomTriangle.drawTriangle(undefined, color);
}
function redraw(yOffset = 0, showTopTriangle = true, showBottomTriangle = true, resetText = false) {
- app.stage.removeChildren();
-
- if (resetText) {
- topTriangle.changeText(topTriangle.currentSSID);
- bottomTriangle.changeText("Backwards");
- }
-
- if (showTopTriangle) {
- topTriangle.drawTriangle(yOffset);
- app.stage.addChild(topTriangle.graphics);
- }
-
- if (showBottomTriangle) {
- bottomTriangle.drawTriangle(yOffset);
- app.stage.addChild(bottomTriangle.graphics);
- }
-
- // Always add cursorFollowingDot to the stage since it's instantiated at the beginning
- if (CTRL_STAT.isWebSocketOpen) {
- app.stage.addChild(cursorFollowingDot.graphics);
- }
+ app.stage.removeChildren();
+
+ if (resetText) {
+ topTriangle.changeText(topTriangle.currentSSID);
+ bottomTriangle.changeText('Backwards');
+ }
+
+ if (showTopTriangle) {
+ topTriangle.drawTriangle(yOffset);
+ app.stage.addChild(topTriangle.graphics);
+ }
+
+ if (showBottomTriangle) {
+ bottomTriangle.drawTriangle(yOffset);
+ app.stage.addChild(bottomTriangle.graphics);
+ }
+
+ // Always add cursorFollowingDot to the stage since it's instantiated at the beginning
+ if (CTRL_STAT.isWebSocketOpen) {
+ app.stage.addChild(cursorFollowingDot.graphics);
+ }
}
-
function drawTopTriangle_BottomRectangle(yOffset = 0) {
- app.stage.removeChildren();
+ app.stage.removeChildren();
- // Hide the input boxes and all related objects when the triangles are pressed
- MotorDataInput.hideInputElements();
+ // Hide the input boxes and all related objects when the triangles are pressed
+ MotorDataInput.hideInputElements();
- topTriangle.drawTriangle(yOffset);
- app.stage.addChild(topTriangle.graphics);
+ topTriangle.drawTriangle(yOffset);
+ app.stage.addChild(topTriangle.graphics);
- // Draw the red area at the bottom
- app.stage.addChild(bottomRectangle.graphics);
+ // Draw the red area at the bottom
+ app.stage.addChild(bottomRectangle.graphics);
- // Always add cursorFollowingDot to the stage since it's instantiated at the beginning
- if (CTRL_STAT.isWebSocketOpen)
- app.stage.addChild(cursorFollowingDot.graphics);
+ // Always add cursorFollowingDot to the stage since it's instantiated at the beginning
+ if (CTRL_STAT.isWebSocketOpen) app.stage.addChild(cursorFollowingDot.graphics);
}
function drawBottomTriangle_TopRectangle(yOffset = 0) {
- app.stage.removeChildren();
-
- // Hide the input boxes and all related objects when the triangles are pressed
- MotorDataInput.hideInputElements();
+ app.stage.removeChildren();
- bottomTriangle.drawTriangle(yOffset);
- app.stage.addChild(bottomTriangle.graphics);
+ // Hide the input boxes and all related objects when the triangles are pressed
+ MotorDataInput.hideInputElements();
- // Draw the red area at the bottom
- app.stage.addChild(topRectangle.graphics);
+ bottomTriangle.drawTriangle(yOffset);
+ app.stage.addChild(bottomTriangle.graphics);
- // Always add cursorFollowingDot to the stage since it's instantiated at the beginning
- if (CTRL_STAT.isWebSocketOpen)
- app.stage.addChild(cursorFollowingDot.graphics);
-}
+ // Draw the red area at the bottom
+ app.stage.addChild(topRectangle.graphics);
+ // Always add cursorFollowingDot to the stage since it's instantiated at the beginning
+ if (CTRL_STAT.isWebSocketOpen) app.stage.addChild(cursorFollowingDot.graphics);
+}
-export { app, changeTrianglesColor, redraw, removeTriangles, drawTopTriangle_BottomRectangle, drawBottomTriangle_TopRectangle }
\ No newline at end of file
+export { app, changeTrianglesColor, redraw, removeTriangles, drawTopTriangle_BottomRectangle, drawBottomTriangle_TopRectangle };
diff --git a/teleop/htm/static/JS/mobileController/mobileController_e_scale_offset_input.js b/teleop/htm/static/JS/mobileController/mobileController_e_scale_offset_input.js
index 78273830..04c0d35f 100644
--- a/teleop/htm/static/JS/mobileController/mobileController_e_scale_offset_input.js
+++ b/teleop/htm/static/JS/mobileController/mobileController_e_scale_offset_input.js
@@ -1,92 +1,81 @@
class MotorDataInput {
- static SCALEINPUT = document.getElementById('scale_input_box_ID');
- static OFFSETINPUT = document.getElementById('offset_input_box_ID');
- static SCALEINPUT_TEXT = document.querySelector('.scale-input-text');
- static OFFSETINPUT_TEXT = document.querySelector('.offset-input-text');
-
-
- static hideInputElements() {
- document.querySelector('.mobile-controller-motor-settings-container').classList.add('hidden');
- }
-
- static showInputElements() {
- document.querySelector('.mobile-controller-motor-settings-container').classList.remove('hidden');
-
- fetch('/teleop/user/options')
- .then(response => response.json())
- .then(data => {
-
- // Get the current values from the backend and place them in the sliders
- this.SCALEINPUT.value = data.vehicle["ras.driver.motor.scale"];
- this.OFFSETINPUT.value = data.vehicle["ras.driver.steering.offset"];
-
- // Update the text next to the slider to show the current value
- this.SCALEINPUT_TEXT.textContent = `${this.SCALEINPUT.value}`;
- this.OFFSETINPUT_TEXT.textContent = `${this.OFFSETINPUT.value}`;
- })
- .catch(error => {
- console.error('Error fetching data:', error);
- });
- }
-
-
- static sendDataToBackend() {
- // Create an array to store non-empty key-value pairs
- let scaleOffsetData = [];
-
- // Check and add non-empty scaleValue
- if (this.SCALEINPUT.value !== "")
- scaleOffsetData.push(["ras.driver.motor.scale", this.SCALEINPUT.value]);
-
- // Check and add non-empty offsetValue
- if (this.OFFSETINPUT.value !== "")
- scaleOffsetData.push(["ras.driver.steering.offset", this.OFFSETINPUT.value]);
-
- // Create the data object that will be sent to the backend via POST
- let data = { "vehicle": scaleOffsetData };
-
- // console.log('Data to send:', data);
-
- // Make POST request to the backend endpoint
- fetch('/teleop/user/options', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(data)
- })
- .then(response => {
- if (!response.ok)
- console.error('Failed to send data.');
- })
- .catch(error => {
- console.error('Error:', error);
- });
- }
+ static SCALEINPUT = document.getElementById('scale_input_box_ID');
+ static OFFSETINPUT = document.getElementById('offset_input_box_ID');
+ static SCALEINPUT_TEXT = document.getElementById('scale-input-text');
+ static OFFSETINPUT_TEXT = document.getElementById('offset-input-text');
+
+ static hideInputElements() {
+ document.getElementById('mobile-controller-top-input-container').classList.add('hidden');
+ }
+
+ static showInputElements() {
+ document.getElementById('mobile-controller-top-input-container').classList.remove('hidden');
+
+ fetch('/teleop/user/options')
+ .then((response) => response.json())
+ .then((data) => {
+ // Get the current values from the backend and place them in the sliders
+ // then update the text next to the slider
+ this.SCALEINPUT.value = this.SCALEINPUT_TEXT.textContent = data.vehicle['ras.driver.motor.scale'];
+ this.OFFSETINPUT.value = this.OFFSETINPUT_TEXT.textContent = data.vehicle['ras.driver.steering.offset'];
+ })
+ .catch((error) => {
+ console.error('Error fetching data:', error);
+ });
+ }
+
+ static sendDataToBackend() {
+ // Create an array to store non-empty key-value pairs
+ let scaleOffsetData = [];
+
+ // Check and add non-empty scaleValue
+ if (this.SCALEINPUT.value !== '') scaleOffsetData.push(['ras.driver.motor.scale', this.SCALEINPUT.value]);
+
+ // Check and add non-empty offsetValue
+ if (this.OFFSETINPUT.value !== '') scaleOffsetData.push(['ras.driver.steering.offset', this.OFFSETINPUT.value]);
+
+ // Create the data object that will be sent to the backend via POST
+ let data = { vehicle: scaleOffsetData };
+
+ // console.log('Data to send:', data);
+
+ // Make POST request to the backend endpoint
+ fetch('/teleop/user/options', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(data),
+ })
+ .then((response) => {
+ if (!response.ok) console.error('Failed to send data.');
+ })
+ .catch((error) => {
+ console.error('Error:', error);
+ });
+ }
}
// When the user drags the slider around, update the text next to the slider
MotorDataInput.SCALEINPUT.addEventListener('input', function () {
- // Update the text next to the slider to show the current value
- MotorDataInput.SCALEINPUT_TEXT.textContent = `${MotorDataInput.SCALEINPUT.value}`;
+ // Update the text next to the slider to show the current value
+ MotorDataInput.SCALEINPUT_TEXT.textContent = `${MotorDataInput.SCALEINPUT.value}`;
});
MotorDataInput.OFFSETINPUT.addEventListener('input', function () {
- // Update the text next to the slider to show the current value
- MotorDataInput.OFFSETINPUT_TEXT.textContent = `${MotorDataInput.OFFSETINPUT.value}`;
+ // Update the text next to the slider to show the current value
+ MotorDataInput.OFFSETINPUT_TEXT.textContent = `${MotorDataInput.OFFSETINPUT.value}`;
});
// When the user removes his finger from the slider, send the current value to the backend
MotorDataInput.SCALEINPUT.addEventListener('change', function () {
- // Send data to the backend
- MotorDataInput.sendDataToBackend();
+ // Send data to the backend
+ MotorDataInput.sendDataToBackend();
});
MotorDataInput.OFFSETINPUT.addEventListener('change', function () {
- // Send data to the backend
- MotorDataInput.sendDataToBackend();
+ // Send data to the backend
+ MotorDataInput.sendDataToBackend();
});
-
-
export { MotorDataInput };
diff --git a/teleop/htm/static/JS/mobileController/mobileController_z_state.js b/teleop/htm/static/JS/mobileController/mobileController_z_state.js
index b13cc13d..0614a386 100644
--- a/teleop/htm/static/JS/mobileController/mobileController_z_state.js
+++ b/teleop/htm/static/JS/mobileController/mobileController_z_state.js
@@ -1,84 +1,90 @@
//Shared State variables that goes between the files/modules used
-import { topTriangle} from "./mobileController_b_shape_triangle.js"
+import { topTriangle } from './mobileController_b_shape_triangle.js';
class MobileControllerState {
- //The starting y coord when the triangles are relocated ()
- #initialYOffset = 0;
- #selectedTriangle = null;
- // Hold the current value for steering and throttle to be sent through the websocket
- // At first we send a default value
- #throttleSteeringJson = { steering: 0, throttle: 0};
- //stands for WebSocket
- #ws;
- #stateErrors;
- #isWebSocketOpen
+ //The starting y coord when the triangles are relocated ()
+ #initialYOffset = 0;
+ #selectedTriangle = null;
+ #followingState = null;
+ // Hold the current value for steering and throttle to be sent through the websocket
+ // At first we send a default value
+ #throttleSteeringJson = { steering: 0, throttle: 0 };
+ //stands for WebSocket
+ #ws;
+ #stateErrors;
+ #isWebSocketOpen;
- #detectedTriangle = "none";
- get midScreen() {
- return window.innerHeight / 2 + this.#initialYOffset;
- }
+ #detectedTriangle = 'none';
+ get midScreen() {
+ return window.innerHeight / 2 + this.#initialYOffset;
+ }
- set initialYOffset(value) {
- this.#initialYOffset = value;
- }
- get initialYOffset() {
- return this.#initialYOffset;
- }
+ set initialYOffset(value) {
+ this.#initialYOffset = value;
+ }
+ get initialYOffset() {
+ return this.#initialYOffset;
+ }
+ set selectedTriangle(value) {
+ this.#selectedTriangle = value;
+ }
+ get selectedTriangle() {
+ return this.#selectedTriangle;
+ }
+ set throttleSteeringJson(value) {
+ this.#throttleSteeringJson = value;
+ }
+ get throttleSteeringJson() {
+ return this.#throttleSteeringJson;
+ }
+
+ set followingState(value) {
+ this.#followingState = value;
+ }
+ get followingState() {
+ return this.#followingState;
+ }
- set selectedTriangle(value) {
- this.#selectedTriangle = value;
- }
- get selectedTriangle() {
- return this.#selectedTriangle;
- }
+ set stateErrors(value) {
+ if (this.#stateErrors != value) {
+ this.#stateErrors = value;
+ topTriangle.changeText(value);
+ }
+ }
+ get stateErrors() {
+ return this.#stateErrors;
+ }
- set throttleSteeringJson(value) {
- this.#throttleSteeringJson = value;
- }
- get throttleSteeringJson() {
- return this.#throttleSteeringJson;
- }
+ set websocket(value) {
+ this.#ws = value;
+ }
+ get websocket() {
+ return this.#ws;
+ }
- set stateErrors(value) {
- if (this.#stateErrors != value) {
- this.#stateErrors = value;
- topTriangle.changeText(value)
- }
- }
- get stateErrors() {
- return this.#stateErrors;
- }
+ set isWebSocketOpen(value) {
+ this.#isWebSocketOpen = value;
+ }
+ get isWebSocketOpen() {
+ return this.#isWebSocketOpen;
+ }
- set websocket(value) {
- this.#ws = value;
- }
- get websocket() {
- return this.#ws;
- }
-
- set isWebSocketOpen(value) {
- this.#isWebSocketOpen = value;
- }
- get isWebSocketOpen() {
- return this.#isWebSocketOpen;
- }
-
- set detectedTriangle(value) {
- if (typeof value === 'string' || value instanceof String) {
- this.#detectedTriangle = value;
- } else {
- console.error(`Value for (detectedTriangle) must be string, got ${value}`);
- }
- }
- get detectedTriangle() {
- return this.#detectedTriangle;
- }
+ set detectedTriangle(value) {
+ if (typeof value === 'string' || value instanceof String) {
+ this.#detectedTriangle = value;
+ } else {
+ console.error(`Value for (detectedTriangle) must be string, got ${value}`);
+ }
+ }
+ get detectedTriangle() {
+ return this.#detectedTriangle;
+ }
}
const sharedState = new MobileControllerState();
// Shared instance will make sure that all the imports can access the same value without change in them
-export default sharedState;
\ No newline at end of file
+export default sharedState;
diff --git a/teleop/htm/templates/mobile_controller_ui.html b/teleop/htm/templates/mobile_controller_ui.html
index 7a71540e..f75264e7 100644
--- a/teleop/htm/templates/mobile_controller_ui.html
+++ b/teleop/htm/templates/mobile_controller_ui.html
@@ -10,46 +10,38 @@