diff --git a/.github/actions/deps/external/action.yml b/.github/actions/deps/external/action.yml index 81e73f871ef..15a3aeb5084 100644 --- a/.github/actions/deps/external/action.yml +++ b/.github/actions/deps/external/action.yml @@ -26,7 +26,6 @@ runs: inputs.port != 'zephyr-cp' uses: carlosperate/arm-none-eabi-gcc-action@v1 with: - # When changing this update what Windows grabs too! release: '15.2.Rel1' # espressif diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f28591e4f56..47b19a8fa7b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,7 +23,6 @@ jobs: outputs: docs: ${{ steps.set-matrix.outputs.docs }} ports: ${{ steps.set-matrix.outputs.ports }} - windows: ${{ steps.set-matrix.outputs.windows }} cp-version: ${{ steps.set-up-submodules.outputs.version }} steps: - name: Dump GitHub context @@ -214,113 +213,6 @@ jobs: [ -z "$TWINE_USERNAME" ] || echo "Uploading dev release to PyPi" [ -z "$TWINE_USERNAME" ] || twine upload circuitpython-stubs/dist/* - windows: - runs-on: windows-2022 - needs: scheduler - if: needs.scheduler.outputs.windows == 'True' - env: - CP_VERSION: ${{ needs.scheduler.outputs.cp-version }} - defaults: - run: - # We define a custom shell script here, although `msys2.cmd` does neither exist nor is it available in the PATH yet - shell: msys2 {0} - steps: - # We want to change the configuration of the git command that actions/checkout will be using - # (since it is not possible to set autocrlf through the action yet, see actions/checkout#226). - - run: git config --global core.autocrlf input - shell: bash - - name: Check python coding (cmd) - run: python -c "import sys, locale; print(sys.getdefaultencoding(), locale.getpreferredencoding(False))" - shell: cmd - # We use a JS Action, which calls the system terminal or other custom terminals directly, if required - - uses: msys2/setup-msys2@v2 - with: - install: base-devel git wget unzip gcc python-pip - # The goal of this was to test how things worked when the default file encoding (locale.getpreferedencoding()) - # was not UTF-8. However, msys2 python does use utf-8 as the preferred file encoding, and using actions/setup-python - # python3.8 gave a broken build, so we're not really testing what we wanted to test. - # However, commandline length limits are being tested so that does some good. - - name: Check python coding (msys2) - run: | - locale -v - which python; python --version - python -c "import sys, locale; print(sys.getdefaultencoding(), locale.getpreferredencoding(False))" - which python3; python3 --version - python3 -c "import sys, locale; print(sys.getdefaultencoding(), locale.getpreferredencoding(False))" - - name: Install dependencies - run: | - wget --no-verbose -O gcc-arm.zip https://developer.arm.com/-/media/Files/downloads/gnu/15.2.rel1/binrel/arm-gnu-toolchain-15.2.rel1-mingw-w64-i686-arm-none-eabi.zip - unzip -q -d /tmp/arm-gnu-toolchain gcc-arm.zip - tar -C /tmp/arm-gnu-toolchain -cf - . | tar -C /usr/local -xf - - # We could use a venv instead, but that requires entering the venv on each run step - # that runs in its own shell. There are some actions that help with that, but not for msys2 - # that I can find. (dhalbert) - pip install --break-system-packages wheel - # requirements-dev.txt doesn't install on windows. (with msys2 python) - # instead, pick a subset for what we want to do - # jsonschema is needed by mbedtls' generate_driver_wrappers.py. Pin it below 4.18: - # 4.18 and later depend on rpds-py, a Rust extension with no wheel for msys2 python - # (SOABI cpython-3xx-x86_64-cygwin), and maturin refuses to build it from source - # ("Unsupported platform: x86_64-cygwin"). 4.17.3 uses attrs and pyrsistent instead, - # both of which install without a Rust toolchain. - pip install --break-system-packages cascadetoml jinja2 typer click intelhex 'jsonschema<4.18' - # check that installed packages work....? - which python; python --version; python -c "import cascadetoml" - which python3; python3 --version; python3 -c "import cascadetoml" - - name: Set up repository - uses: actions/checkout@v6 - with: - submodules: false - show-progress: false - fetch-depth: 1 - persist-credentials: false - - name: Set up submodules - uses: ./.github/actions/deps/submodules - - name: build mpy-cross - run: make -j4 -C mpy-cross - - name: build rp2040 - run: make -j4 -C ports/raspberrypi BOARD=adafruit_feather_rp2040 TRANSLATION=de_DE - - name: build samd21 - run: make -j4 -C ports/atmel-samd BOARD=feather_m0_express TRANSLATION=zh_Latn_pinyin - - name: build samd51 - run: make -j4 -C ports/atmel-samd BOARD=feather_m4_express TRANSLATION=es - - name: build nordic - run: make -j4 -C ports/nordic BOARD=feather_nrf52840_express TRANSLATION=fr - - name: build stm - run: make -j4 -C ports/stm BOARD=feather_stm32f405_express TRANSLATION=pt_BR - # I gave up trying to do esp builds on windows when I saw - # ERROR: Platform MINGW64_NT-10.0-17763-x86_64 appears to be unsupported - # https://github.com/espressif/esp-idf/issues/7062 - - windows-zephyr: - strategy: - matrix: - os: [windows-2022, windows-2025] - runs-on: ${{ matrix.os }} - needs: scheduler - if: needs.scheduler.outputs.windows == 'True' - env: - CP_VERSION: ${{ needs.scheduler.outputs.cp-version }} - steps: - - name: Set up repository - uses: actions/checkout@v6 - with: - submodules: false - show-progress: false - fetch-depth: 1 - persist-credentials: false - - uses: actions/setup-python@v6 - with: - python-version: '3.13' - - name: Set up Zephyr - uses: ./.github/actions/deps/ports/zephyr-cp - - name: Set up submodules - uses: ./.github/actions/deps/submodules - - name: build mpy-cross - run: make -j4 -C mpy-cross - - name: build ek_ra8d1 - run: make -j4 -C ports/zephyr-cp BOARD=renesas_ek_ra8d1 - ports: needs: [scheduler, mpy-cross, tests] if: needs.scheduler.outputs.ports != '{}' diff --git a/.github/workflows/notify-on-issue-label.yml b/.github/workflows/notify-on-issue-label.yml index 2b229156811..2465851e35b 100644 --- a/.github/workflows/notify-on-issue-label.yml +++ b/.github/workflows/notify-on-issue-label.yml @@ -10,7 +10,7 @@ jobs: permissions: issues: write steps: - - uses: tekktrik/issue-labeled-ping@v2 + - uses: tekktrik/issue-labeled-ping@v3 with: github-token: ${{ secrets.GITHUB_TOKEN }} user: v923z diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 5bb3aaee736..711b5f321b6 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -99,5 +99,7 @@ jobs: working-directory: ports/zephyr-cp/tools/bsim - name: Build native_nrf5340bsim run: make -C ports/zephyr-cp -j2 BOARD=native_nrf5340bsim + - name: Build native_nrf54lm20bsim + run: make -C ports/zephyr-cp -j2 BOARD=native_nrf54lm20bsim - name: Run Zephyr tests run: make -C ports/zephyr-cp test diff --git a/.gitmodules b/.gitmodules index f241a4c7eae..5d544b18cce 100644 --- a/.gitmodules +++ b/.gitmodules @@ -421,3 +421,6 @@ [submodule "frozen/CircuitPython_edupico2_paj7620"] path = frozen/CircuitPython_edupico2_paj7620 url = https://github.com/CytronTechnologies/CircuitPython_edupico2_paj7620.git +[submodule "frozen/Adafruit_CircuitPython_BLE_File_Transfer"] + path = frozen/Adafruit_CircuitPython_BLE_File_Transfer + url = https://github.com/adafruit/Adafruit_CircuitPython_BLE_File_Transfer.git diff --git a/data/nvm.toml b/data/nvm.toml index 8bca037b052..a2a0c03c221 160000 --- a/data/nvm.toml +++ b/data/nvm.toml @@ -1 +1 @@ -Subproject commit 8bca037b052a4a4dc46a56a25a1b802652ee3f47 +Subproject commit a2a0c03c221a9bc37992147b5d4ad785a6e467fe diff --git a/devices/ble_hci/common-hal/_bleio/Adapter.c b/devices/ble_hci/common-hal/_bleio/Adapter.c index aaff47f42a6..005dd0aeb03 100644 --- a/devices/ble_hci/common-hal/_bleio/Adapter.c +++ b/devices/ble_hci/common-hal/_bleio/Adapter.c @@ -29,10 +29,6 @@ #include "shared-bindings/_bleio/ScanEntry.h" #include "shared-bindings/time/__init__.h" -#if CIRCUITPY_SETTINGS_TOML -#include "shared-bindings/os/__init__.h" -#endif - #define MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION)) #define SEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000000) / (RESOLUTION)) #define UNITS_TO_SEC(TIME, RESOLUTION) (((TIME)*(RESOLUTION)) / 1000000) @@ -247,9 +243,6 @@ static void check_enabled(bleio_adapter_obj_t *adapter) { // return true; // } -// Includes trailing null. -char default_ble_name[] = { 'C', 'I', 'R', 'C', 'U', 'I', 'T', 'P', 'Y', 0, 0, 0, 0, 0}; - static void _adapter_set_name(bleio_adapter_obj_t *self, mp_obj_str_t *name_obj) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(name_obj, &bufinfo, MP_BUFFER_READ); @@ -261,29 +254,7 @@ static void _adapter_set_name(bleio_adapter_obj_t *self, mp_obj_str_t *name_obj) static void bleio_adapter_hci_init(bleio_adapter_obj_t *self) { self->name = NULL; - mp_int_t name_len = 0; - - #if CIRCUITPY_SETTINGS_TOML - mp_obj_t name = common_hal_os_getenv("CIRCUITPY_BLE_NAME", mp_const_none); - if (name != mp_const_none) { - mp_arg_validate_type_string(name, MP_QSTR_CIRCUITPY_BLE_NAME); - self->name = name; - } - #endif - - if (!self->name) { - name_len = sizeof(default_ble_name) - 1; - bt_addr_t addr; - hci_check_error(hci_read_bd_addr(&addr)); - - default_ble_name[name_len - 4] = nibble_to_hex_lower[addr.val[1] >> 4 & 0xf]; - default_ble_name[name_len - 3] = nibble_to_hex_lower[addr.val[1] & 0xf]; - default_ble_name[name_len - 2] = nibble_to_hex_lower[addr.val[0] >> 4 & 0xf]; - default_ble_name[name_len - 1] = nibble_to_hex_lower[addr.val[0] & 0xf]; - // default_ble_name[name_len] will be zero. - self->name = mp_obj_new_str(default_ble_name, (uint8_t)name_len); - } - _adapter_set_name(self, self->name); + bleio_adapter_reset_name(self); // Get version information. if (hci_read_local_version(&self->hci_version, &self->hci_revision, &self->lmp_version, @@ -396,18 +367,14 @@ bleio_address_obj_t *common_hal_bleio_adapter_get_address(bleio_adapter_obj_t *s bt_addr_t addr; hci_check_error(hci_read_bd_addr(&addr)); - bleio_address_obj_t *address = mp_obj_malloc(bleio_address_obj_t, &bleio_address_type); - - common_hal_bleio_address_construct(address, addr.val, BT_ADDR_LE_PUBLIC); - return address; + // The cached address is refreshed on every call. + common_hal_bleio_address_construct(&self->address, addr.val, BT_ADDR_LE_PUBLIC); + self->address.base.type = &bleio_address_type; + return &self->address; } bool common_hal_bleio_adapter_set_address(bleio_adapter_obj_t *self, bleio_address_obj_t *address) { - mp_buffer_info_t bufinfo; - if (!mp_get_buffer(address->bytes, &bufinfo, MP_BUFFER_READ)) { - return false; - } - return hci_le_set_random_address(bufinfo.buf) == HCI_OK; + return hci_le_set_random_address(address->bytes) == HCI_OK; } mp_obj_str_t *common_hal_bleio_adapter_get_name(bleio_adapter_obj_t *self) { @@ -662,11 +629,8 @@ uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, // Copy peer address, if supplied. if (directed_to) { - mp_buffer_info_t bufinfo; - if (mp_get_buffer(directed_to->bytes, &bufinfo, MP_BUFFER_READ)) { - peer_addr.type = directed_to->type; - memcpy(&peer_addr.a.val, bufinfo.buf, sizeof(peer_addr.a.val)); - } + peer_addr.type = directed_to->type; + memcpy(&peer_addr.a.val, directed_to->bytes, sizeof(peer_addr.a.val)); } bool extended = diff --git a/devices/ble_hci/common-hal/_bleio/Adapter.h b/devices/ble_hci/common-hal/_bleio/Adapter.h index fda1c11d6cb..c018c676171 100644 --- a/devices/ble_hci/common-hal/_bleio/Adapter.h +++ b/devices/ble_hci/common-hal/_bleio/Adapter.h @@ -11,6 +11,7 @@ #include "py/obj.h" #include "py/objtuple.h" +#include "shared-bindings/_bleio/Address.h" #include "shared-bindings/_bleio/Characteristic.h" #include "shared-bindings/_bleio/Connection.h" #include "shared-bindings/_bleio/ScanResults.h" @@ -65,6 +66,10 @@ typedef struct _bleio_adapter_obj_t { // the service they belong to. This vets that. uint16_t last_added_service_handle; uint16_t last_added_characteristic_handle; + // Cached local address returned by common_hal_bleio_adapter_get_address(). + // Stored inline so it never needs to allocate, even when read before the + // heap is available (e.g. from bleio_adapter_reset_name). + bleio_address_obj_t address; } bleio_adapter_obj_t; uint16_t bleio_adapter_add_attribute(bleio_adapter_obj_t *adapter, mp_obj_t *attribute); diff --git a/devices/ble_hci/common-hal/_bleio/PacketBuffer.c b/devices/ble_hci/common-hal/_bleio/PacketBuffer.c index 771a1509f39..f29f4dd88a7 100644 --- a/devices/ble_hci/common-hal/_bleio/PacketBuffer.c +++ b/devices/ble_hci/common-hal/_bleio/PacketBuffer.c @@ -245,6 +245,8 @@ void common_hal_bleio_packet_buffer_deinit(bleio_packet_buffer_obj_t *self) { if (!common_hal_bleio_packet_buffer_deinited(self)) { bleio_characteristic_clear_observer(self->characteristic); ringbuf_deinit(&self->ringbuf); + // Mark as deinited, so common_hal_bleio_packet_buffer_deinited() reports it. + self->characteristic = NULL; } } diff --git a/devices/ble_hci/common-hal/_bleio/__init__.c b/devices/ble_hci/common-hal/_bleio/__init__.c index f9fdbc50f64..29078944aaa 100644 --- a/devices/ble_hci/common-hal/_bleio/__init__.c +++ b/devices/ble_hci/common-hal/_bleio/__init__.c @@ -30,7 +30,7 @@ bool vm_used_ble; // switch (sec_status) { // case BLE_GAP_SEC_STATUS_UNSPECIFIED: -// mp_raise_bleio_SecurityError(MP_ERROR_TEXT("Unspecified issue. Can be that the pairing prompt on the other device was declined or ignored.")); +// mp_raise_bleio_SecurityError(MP_ERROR_TEXT("Unspecified issue. The pairing request on the other device may have been declined or ignored.")); // return; // default: // mp_raise_bleio_SecurityError(MP_ERROR_TEXT("Unknown security error: 0x%04x"), sec_status); @@ -38,6 +38,13 @@ bool vm_used_ble; // } void common_hal_bleio_init(void) { + // Called on every import of _bleio. This is the only initialization HCI gets: + // unlike other ports, nothing runs at board startup, because HCI is unusable + // until user code supplies an adapter (UART, etc.) anyway. + // Create a UUID object for all CCCD's. + cccd_uuid.base.type = &bleio_uuid_type; + common_hal_bleio_uuid_construct(&cccd_uuid, BLE_UUID_CCCD, NULL); + bleio_hci_reset(); } void bleio_user_reset(void) { @@ -47,10 +54,6 @@ void bleio_user_reset(void) { // Turn off BLE on a reset or reload. void bleio_reset(void) { - // Create a UUID object for all CCCD's. - cccd_uuid.base.type = &bleio_uuid_type; - common_hal_bleio_uuid_construct(&cccd_uuid, BLE_UUID_CCCD, NULL); - bleio_hci_reset(); if (!common_hal_bleio_adapter_get_enabled(&common_hal_bleio_adapter_obj)) { diff --git a/docs/environment.rst b/docs/environment.rst index 4bf89bf774a..6ba541ba34c 100644 --- a/docs/environment.rst +++ b/docs/environment.rst @@ -134,7 +134,8 @@ CIRCUITPY_SDCARD_USB (boolean) Present a mounted SD card as a USB MSC device. If the board has default pins for an SD card socket, the card is mounted automatically on startup. Only one card can be presented. -Defaults to ``true``. +Defaults to ``true``, except on the Adafruit Memento board, where it defaults to ``false`` +for now to avoid a bug. SD card presentation can slow down board startup, so set this to ``false`` if you don't need this feature. diff --git a/frozen/Adafruit_CircuitPython_BLE_File_Transfer b/frozen/Adafruit_CircuitPython_BLE_File_Transfer new file mode 160000 index 00000000000..14c0870bc91 --- /dev/null +++ b/frozen/Adafruit_CircuitPython_BLE_File_Transfer @@ -0,0 +1 @@ +Subproject commit 14c0870bc915aba90cf6e8a4002adeb563bc95fe diff --git a/frozen/Adafruit_CircuitPython_Bitmap_Font b/frozen/Adafruit_CircuitPython_Bitmap_Font index fe0cdfd5501..94bec4ba5ee 160000 --- a/frozen/Adafruit_CircuitPython_Bitmap_Font +++ b/frozen/Adafruit_CircuitPython_Bitmap_Font @@ -1 +1 @@ -Subproject commit fe0cdfd5501070f5ca47b3e66dfd15e62c54011e +Subproject commit 94bec4ba5ee9bbc1f9a72281e3085ab10b5efd3a diff --git a/frozen/Adafruit_CircuitPython_Display_Text b/frozen/Adafruit_CircuitPython_Display_Text index eae352b0970..6761a7f0a3c 160000 --- a/frozen/Adafruit_CircuitPython_Display_Text +++ b/frozen/Adafruit_CircuitPython_Display_Text @@ -1 +1 @@ -Subproject commit eae352b09700e57b78507aace7ff682cb1beff5a +Subproject commit 6761a7f0a3ce7549c9887caee2c85bceef739d00 diff --git a/frozen/Adafruit_CircuitPython_ESP32SPI b/frozen/Adafruit_CircuitPython_ESP32SPI index b0e2124a175..64f4695cc44 160000 --- a/frozen/Adafruit_CircuitPython_ESP32SPI +++ b/frozen/Adafruit_CircuitPython_ESP32SPI @@ -1 +1 @@ -Subproject commit b0e2124a175f421bb07ac5cc72b81f6209966a3b +Subproject commit 64f4695cc4457df0718e1a6460082381359c3562 diff --git a/frozen/Adafruit_CircuitPython_Motor b/frozen/Adafruit_CircuitPython_Motor index 20ebf51fe0d..dc0c88c1d7c 160000 --- a/frozen/Adafruit_CircuitPython_Motor +++ b/frozen/Adafruit_CircuitPython_Motor @@ -1 +1 @@ -Subproject commit 20ebf51fe0d2bb48b1780c3c9bd74c16c73bc02e +Subproject commit dc0c88c1d7ca386a434d17c65a1e5efa5fc31a5f diff --git a/frozen/Adafruit_CircuitPython_Register b/frozen/Adafruit_CircuitPython_Register index e168e97a2c9..6ebae944c97 160000 --- a/frozen/Adafruit_CircuitPython_Register +++ b/frozen/Adafruit_CircuitPython_Register @@ -1 +1 @@ -Subproject commit e168e97a2c9713ec0f82cb6171461873dfffd46d +Subproject commit 6ebae944c97ff8f9394ceb2c634a32aef903e5c4 diff --git a/frozen/Adafruit_CircuitPython_SSD1680 b/frozen/Adafruit_CircuitPython_SSD1680 index f17e62d2466..d3eb6f27c0d 160000 --- a/frozen/Adafruit_CircuitPython_SSD1680 +++ b/frozen/Adafruit_CircuitPython_SSD1680 @@ -1 +1 @@ -Subproject commit f17e62d246656b3f956232c368bc05d966988d9a +Subproject commit d3eb6f27c0d89e7e8a08bdaf0c6a6e40f7fc512c diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index dbd6d902194..c7275f7aee3 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -1041,7 +1041,7 @@ msgstr "" #: ports/espressif/common-hal/sdioio/SDCard.c #: ports/raspberrypi/common-hal/sdioio/SDCard.c #: ports/stm/common-hal/sdioio/SDCard.c shared-bindings/floppyio/__init__.c -#: shared-module/sdcardio/SDCard.c +#: shared-bindings/picogame/Canvas.c shared-module/sdcardio/SDCard.c #, c-format msgid "Buffer must be a multiple of %d bytes" msgstr "" @@ -1200,6 +1200,7 @@ msgstr "" #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c +#: ports/zephyr-cp/common-hal/_bleio/Characteristic.c msgid "No CCCD for this Characteristic" msgstr "" @@ -1222,11 +1223,13 @@ msgstr "" #: ports/espressif/common-hal/_bleio/PacketBuffer.c #: ports/nordic/common-hal/_bleio/PacketBuffer.c +#: ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c msgid "Writes not supported on Characteristic" msgstr "" #: ports/espressif/common-hal/_bleio/PacketBuffer.c #: ports/nordic/common-hal/_bleio/PacketBuffer.c +#: ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c msgid "Total data to write is larger than %q" msgstr "" @@ -1241,6 +1244,9 @@ msgstr "" #: ports/espressif/common-hal/_bleio/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Characteristic.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c +#: ports/zephyr-cp/common-hal/_bleio/Descriptor.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" msgstr "" @@ -1470,6 +1476,7 @@ msgid "Requested resource not found" msgstr "" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/raspberrypi/common-hal/picogame/Display.c msgid "Operation or feature not supported" msgstr "" @@ -1676,7 +1683,8 @@ msgstr "" msgid "Only IPv4 addresses supported" msgstr "" -#: ports/mimxrt10xx/common-hal/busio/SPI.c shared-bindings/busio/SPI.c +#: ports/mimxrt10xx/common-hal/busio/SPI.c +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/busio/SPI.c msgid "Must provide MISO or MOSI pin" msgstr "" @@ -1732,12 +1740,14 @@ msgid "Unknown gatt error: 0x%04x" msgstr "" #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c msgid "" -"Unspecified issue. Can be that the pairing prompt on the other device was " +"Unspecified issue. The pairing request on the other device may have been " "declined or ignored." msgstr "" #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c #, c-format msgid "Unknown security error: 0x%04x" msgstr "" @@ -1807,9 +1817,9 @@ msgstr "" #: ports/raspberrypi/bindings/cyw43/__init__.c py/argcheck.c py/objexcept.c #: shared-bindings/bitmapfilter/__init__.c shared-bindings/canio/CAN.c -#: shared-bindings/digitalio/Pull.c shared-bindings/supervisor/__init__.c -#: shared-module/audiofilters/Filter.c shared-module/displayio/__init__.c -#: shared-module/synthio/Synthesizer.c +#: shared-bindings/digitalio/Pull.c shared-bindings/picogame/Sprite.c +#: shared-bindings/supervisor/__init__.c shared-module/audiofilters/Filter.c +#: shared-module/displayio/__init__.c shared-module/synthio/Synthesizer.c msgid "%q must be of type %q or %q, not %q" msgstr "" @@ -1838,6 +1848,17 @@ msgstr "" msgid "In-buffer elements must be <= 4 bytes long" msgstr "" +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/bitbangio/I2C.c +#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c +#: shared-bindings/busio/SPI.c +msgid "Function requires lock" +msgstr "" + +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/bitbangio/SPI.c +#: shared-bindings/busio/SPI.c +msgid "buffer slices must be of equal length" +msgstr "" + #: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" @@ -1952,6 +1973,13 @@ msgstr "" msgid "All timers for this pin are in use" msgstr "" +#: ports/raspberrypi/common-hal/picogame/Display.c py/argcheck.c py/runtime.c +#: shared-bindings/bitmapfilter/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c +msgid "%q must be of type %q, not %q" +msgstr "" + #: ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c msgid "Pins must be sequential GPIO pins" msgstr "" @@ -2238,6 +2266,10 @@ msgstr "" msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" msgstr "" +#: ports/zephyr-cp/common-hal/usb_hid/Device.c shared-module/usb_hid/Device.c +msgid "USB error" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -2286,7 +2318,7 @@ msgid "extra keyword arguments given" msgstr "" #: py/argcheck.c shared-bindings/_stage/__init__.c -#: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/digitalio/DigitalInOut.c shared-bindings/picogame/__init__.c msgid "argument num/types mismatch" msgstr "" @@ -2307,12 +2339,6 @@ msgstr "" msgid "%q must be <= %d" msgstr "" -#: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c -#: shared-module/synthio/__init__.c -msgid "%q must be of type %q, not %q" -msgstr "" - #: py/argcheck.c msgid "%q length must be %d-%d" msgstr "" @@ -2862,7 +2888,9 @@ msgstr "" msgid "schedule queue full" msgstr "" -#: py/modstruct.c shared-module/struct/__init__.c +#: py/modstruct.c shared-bindings/picogame/Bitmap.c +#: shared-bindings/picogame/Canvas.c shared-bindings/picogame/Framebuffer.c +#: shared-module/picogame/__init__.c shared-module/struct/__init__.c msgid "buffer too small" msgstr "" @@ -3726,6 +3754,7 @@ msgid "file must be a file opened in byte mode" msgstr "" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/Flanger.c #: shared-bindings/audiodelays/GranularPitchShift.c #: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c @@ -3743,10 +3772,6 @@ msgstr "" msgid "bits_per_sample must be 16" msgstr "" -#: shared-bindings/audioi2sin/I2SIn.c -msgid "%q requires %q" -msgstr "" - #: shared-bindings/audioi2sin/I2SIn.c #, c-format msgid "invalid destination buffer, must be an array of type: %c" @@ -3756,15 +3781,6 @@ msgstr "" msgid "%q and %q must be different" msgstr "" -#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c -#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c -msgid "Function requires lock" -msgstr "" - -#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/SPI.c -msgid "buffer slices must be of equal length" -msgstr "" - #: shared-bindings/bitmapfilter/__init__.c msgid "" "weights must be a sequence with an odd square number of elements (usually 9 " @@ -4082,6 +4098,11 @@ msgstr "" msgid "Specify exactly one of data0 or data_pins" msgstr "" +#: shared-bindings/picogame/Bitmap.c shared-module/msgpack/__init__.c +#: supervisor/shared/settings.c +msgid "Invalid format" +msgstr "" + #: shared-bindings/ps2io/Ps2.c msgid "Failed sending command." msgstr "" @@ -4177,6 +4198,10 @@ msgstr "" msgid "timestamp out of range for platform time_t" msgstr "" +#: shared-bindings/traceback/__init__.c +msgid "Supply both or neither of %q and %q" +msgstr "" + #: shared-bindings/traceback/__init__.c msgid "file write is not available" msgstr "" @@ -4401,10 +4426,6 @@ msgstr "" msgid "no default packer" msgstr "" -#: shared-module/msgpack/__init__.c supervisor/shared/settings.c -msgid "Invalid format" -msgstr "" - #: shared-module/paralleldisplaybus/ParallelBus.c msgid "" "This microcontroller only supports data0=, not data_pins=, because it " @@ -4508,10 +4529,6 @@ msgstr "" msgid "USB busy" msgstr "" -#: shared-module/usb_hid/Device.c -msgid "USB error" -msgstr "" - #: shared-module/vectorio/Circle.c shared-module/vectorio/Polygon.c #: shared-module/vectorio/Rectangle.c msgid "can only have one parent" diff --git a/locale/cs.po b/locale/cs.po index 20ecba5b49b..18b5dea7fbb 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -1049,7 +1049,7 @@ msgstr "%q: selhání %d" #: ports/espressif/common-hal/sdioio/SDCard.c #: ports/raspberrypi/common-hal/sdioio/SDCard.c #: ports/stm/common-hal/sdioio/SDCard.c shared-bindings/floppyio/__init__.c -#: shared-module/sdcardio/SDCard.c +#: shared-bindings/picogame/Canvas.c shared-module/sdcardio/SDCard.c #, c-format msgid "Buffer must be a multiple of %d bytes" msgstr "" @@ -1208,6 +1208,7 @@ msgstr "" #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c +#: ports/zephyr-cp/common-hal/_bleio/Characteristic.c msgid "No CCCD for this Characteristic" msgstr "Žádné CCCD pro tuto charakteristiku" @@ -1230,11 +1231,13 @@ msgstr "" #: ports/espressif/common-hal/_bleio/PacketBuffer.c #: ports/nordic/common-hal/_bleio/PacketBuffer.c +#: ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c msgid "Writes not supported on Characteristic" msgstr "" #: ports/espressif/common-hal/_bleio/PacketBuffer.c #: ports/nordic/common-hal/_bleio/PacketBuffer.c +#: ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c msgid "Total data to write is larger than %q" msgstr "Velikost dat k zápisu je větší než %q" @@ -1249,6 +1252,9 @@ msgstr "Chybný BLE parametr" #: ports/espressif/common-hal/_bleio/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Characteristic.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c +#: ports/zephyr-cp/common-hal/_bleio/Descriptor.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" msgstr "Nepřipojený" @@ -1481,6 +1487,7 @@ msgid "Requested resource not found" msgstr "Požadovaný zdroj nebyl nalezen" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/raspberrypi/common-hal/picogame/Display.c msgid "Operation or feature not supported" msgstr "Operace nebo funkce není podporována" @@ -1687,7 +1694,8 @@ msgstr "Chyba alokace paměti WiFi" msgid "Only IPv4 addresses supported" msgstr "Pouze IPv4 adresy podporovány" -#: ports/mimxrt10xx/common-hal/busio/SPI.c shared-bindings/busio/SPI.c +#: ports/mimxrt10xx/common-hal/busio/SPI.c +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/busio/SPI.c msgid "Must provide MISO or MOSI pin" msgstr "Musí poskytnout pin MISO nebo MOSI" @@ -1745,12 +1753,14 @@ msgid "Unknown gatt error: 0x%04x" msgstr "" #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c msgid "" -"Unspecified issue. Can be that the pairing prompt on the other device was " +"Unspecified issue. The pairing request on the other device may have been " "declined or ignored." msgstr "" #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c #, c-format msgid "Unknown security error: 0x%04x" msgstr "Neznámá bezpečnostní chyba: 0x%04x" @@ -1820,9 +1830,9 @@ msgstr "Konverze audia není implementována" #: ports/raspberrypi/bindings/cyw43/__init__.c py/argcheck.c py/objexcept.c #: shared-bindings/bitmapfilter/__init__.c shared-bindings/canio/CAN.c -#: shared-bindings/digitalio/Pull.c shared-bindings/supervisor/__init__.c -#: shared-module/audiofilters/Filter.c shared-module/displayio/__init__.c -#: shared-module/synthio/Synthesizer.c +#: shared-bindings/digitalio/Pull.c shared-bindings/picogame/Sprite.c +#: shared-bindings/supervisor/__init__.c shared-module/audiofilters/Filter.c +#: shared-module/displayio/__init__.c shared-module/synthio/Synthesizer.c msgid "%q must be of type %q or %q, not %q" msgstr "%q musí být typu %q nebo %q, ne %q" @@ -1851,6 +1861,17 @@ msgstr "" msgid "In-buffer elements must be <= 4 bytes long" msgstr "Elementy v bufferu musí být <= 4 bajty" +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/bitbangio/I2C.c +#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c +#: shared-bindings/busio/SPI.c +msgid "Function requires lock" +msgstr "Funkce vyžaduje zámek" + +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/bitbangio/SPI.c +#: shared-bindings/busio/SPI.c +msgid "buffer slices must be of equal length" +msgstr "" + #: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" @@ -1965,6 +1986,13 @@ msgstr "Není možné zapisovat do paměti jen pro čtení" msgid "All timers for this pin are in use" msgstr "Všechny časovače pro tento pin jsou používány" +#: ports/raspberrypi/common-hal/picogame/Display.c py/argcheck.c py/runtime.c +#: shared-bindings/bitmapfilter/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c +msgid "%q must be of type %q, not %q" +msgstr "%q musí být typu %q, ne %q" + #: ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c msgid "Pins must be sequential GPIO pins" msgstr "" @@ -2252,6 +2280,10 @@ msgstr "" msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" msgstr "" +#: ports/zephyr-cp/common-hal/usb_hid/Device.c shared-module/usb_hid/Device.c +msgid "USB error" +msgstr "Chyba USB" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -2300,7 +2332,7 @@ msgid "extra keyword arguments given" msgstr "" #: py/argcheck.c shared-bindings/_stage/__init__.c -#: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/digitalio/DigitalInOut.c shared-bindings/picogame/__init__.c msgid "argument num/types mismatch" msgstr "" @@ -2321,12 +2353,6 @@ msgstr "%q musí být >= %d" msgid "%q must be <= %d" msgstr "%q musí být <= %d" -#: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c -#: shared-module/synthio/__init__.c -msgid "%q must be of type %q, not %q" -msgstr "%q musí být typu %q, ne %q" - #: py/argcheck.c msgid "%q length must be %d-%d" msgstr "%q délka musí být %d-%d" @@ -2881,7 +2907,9 @@ msgstr "záporný faktoriál" msgid "schedule queue full" msgstr "" -#: py/modstruct.c shared-module/struct/__init__.c +#: py/modstruct.c shared-bindings/picogame/Bitmap.c +#: shared-bindings/picogame/Canvas.c shared-bindings/picogame/Framebuffer.c +#: shared-module/picogame/__init__.c shared-module/struct/__init__.c msgid "buffer too small" msgstr "" @@ -3746,6 +3774,7 @@ msgid "file must be a file opened in byte mode" msgstr "" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/Flanger.c #: shared-bindings/audiodelays/GranularPitchShift.c #: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c @@ -3763,10 +3792,6 @@ msgstr "" msgid "bits_per_sample must be 16" msgstr "" -#: shared-bindings/audioi2sin/I2SIn.c -msgid "%q requires %q" -msgstr "" - #: shared-bindings/audioi2sin/I2SIn.c #, c-format msgid "invalid destination buffer, must be an array of type: %c" @@ -3776,15 +3801,6 @@ msgstr "" msgid "%q and %q must be different" msgstr "%q a %q musí být rozdílné" -#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c -#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c -msgid "Function requires lock" -msgstr "Funkce vyžaduje zámek" - -#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/SPI.c -msgid "buffer slices must be of equal length" -msgstr "" - #: shared-bindings/bitmapfilter/__init__.c msgid "" "weights must be a sequence with an odd square number of elements (usually 9 " @@ -4102,6 +4118,11 @@ msgstr "" msgid "Specify exactly one of data0 or data_pins" msgstr "Specifikuj přesně jeden z data0 nebo data_pins" +#: shared-bindings/picogame/Bitmap.c shared-module/msgpack/__init__.c +#: supervisor/shared/settings.c +msgid "Invalid format" +msgstr "Špatný formát" + #: shared-bindings/ps2io/Ps2.c msgid "Failed sending command." msgstr "Nepodařilo se odeslat příkaz." @@ -4197,6 +4218,10 @@ msgstr "Soubor nenalezen" msgid "timestamp out of range for platform time_t" msgstr "" +#: shared-bindings/traceback/__init__.c +msgid "Supply both or neither of %q and %q" +msgstr "" + #: shared-bindings/traceback/__init__.c msgid "file write is not available" msgstr "zápis do souboru není dostupný" @@ -4423,10 +4448,6 @@ msgstr "" msgid "no default packer" msgstr "" -#: shared-module/msgpack/__init__.c supervisor/shared/settings.c -msgid "Invalid format" -msgstr "Špatný formát" - #: shared-module/paralleldisplaybus/ParallelBus.c msgid "" "This microcontroller only supports data0=, not data_pins=, because it " @@ -4530,10 +4551,6 @@ msgstr "Konfigurace není nastavena" msgid "USB busy" msgstr "USB zaneprázdněno" -#: shared-module/usb_hid/Device.c -msgid "USB error" -msgstr "Chyba USB" - #: shared-module/vectorio/Circle.c shared-module/vectorio/Polygon.c #: shared-module/vectorio/Rectangle.c msgid "can only have one parent" diff --git a/locale/el.po b/locale/el.po index 83d329b7a41..d97ea22b03f 100644 --- a/locale/el.po +++ b/locale/el.po @@ -1052,7 +1052,7 @@ msgstr "%q αποτυχία: %d" #: ports/espressif/common-hal/sdioio/SDCard.c #: ports/raspberrypi/common-hal/sdioio/SDCard.c #: ports/stm/common-hal/sdioio/SDCard.c shared-bindings/floppyio/__init__.c -#: shared-module/sdcardio/SDCard.c +#: shared-bindings/picogame/Canvas.c shared-module/sdcardio/SDCard.c #, c-format msgid "Buffer must be a multiple of %d bytes" msgstr "" @@ -1211,6 +1211,7 @@ msgstr "" #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c +#: ports/zephyr-cp/common-hal/_bleio/Characteristic.c msgid "No CCCD for this Characteristic" msgstr "" @@ -1233,11 +1234,13 @@ msgstr "" #: ports/espressif/common-hal/_bleio/PacketBuffer.c #: ports/nordic/common-hal/_bleio/PacketBuffer.c +#: ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c msgid "Writes not supported on Characteristic" msgstr "" #: ports/espressif/common-hal/_bleio/PacketBuffer.c #: ports/nordic/common-hal/_bleio/PacketBuffer.c +#: ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c msgid "Total data to write is larger than %q" msgstr "" @@ -1252,6 +1255,9 @@ msgstr "" #: ports/espressif/common-hal/_bleio/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Characteristic.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c +#: ports/zephyr-cp/common-hal/_bleio/Descriptor.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" msgstr "" @@ -1482,6 +1488,7 @@ msgid "Requested resource not found" msgstr "" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/raspberrypi/common-hal/picogame/Display.c msgid "Operation or feature not supported" msgstr "" @@ -1688,7 +1695,8 @@ msgstr "" msgid "Only IPv4 addresses supported" msgstr "" -#: ports/mimxrt10xx/common-hal/busio/SPI.c shared-bindings/busio/SPI.c +#: ports/mimxrt10xx/common-hal/busio/SPI.c +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/busio/SPI.c msgid "Must provide MISO or MOSI pin" msgstr "" @@ -1744,12 +1752,14 @@ msgid "Unknown gatt error: 0x%04x" msgstr "" #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c msgid "" -"Unspecified issue. Can be that the pairing prompt on the other device was " +"Unspecified issue. The pairing request on the other device may have been " "declined or ignored." msgstr "" #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c #, c-format msgid "Unknown security error: 0x%04x" msgstr "" @@ -1819,9 +1829,9 @@ msgstr "Η μετατροπή ήχου δεν υποστηρίζεται" #: ports/raspberrypi/bindings/cyw43/__init__.c py/argcheck.c py/objexcept.c #: shared-bindings/bitmapfilter/__init__.c shared-bindings/canio/CAN.c -#: shared-bindings/digitalio/Pull.c shared-bindings/supervisor/__init__.c -#: shared-module/audiofilters/Filter.c shared-module/displayio/__init__.c -#: shared-module/synthio/Synthesizer.c +#: shared-bindings/digitalio/Pull.c shared-bindings/picogame/Sprite.c +#: shared-bindings/supervisor/__init__.c shared-module/audiofilters/Filter.c +#: shared-module/displayio/__init__.c shared-module/synthio/Synthesizer.c msgid "%q must be of type %q or %q, not %q" msgstr "%q πρέπει να είναι τύπου %q ή %q, όχι %q" @@ -1850,6 +1860,17 @@ msgstr "" msgid "In-buffer elements must be <= 4 bytes long" msgstr "" +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/bitbangio/I2C.c +#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c +#: shared-bindings/busio/SPI.c +msgid "Function requires lock" +msgstr "" + +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/bitbangio/SPI.c +#: shared-bindings/busio/SPI.c +msgid "buffer slices must be of equal length" +msgstr "" + #: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" @@ -1964,6 +1985,13 @@ msgstr "" msgid "All timers for this pin are in use" msgstr "Όλοι οι χρονιστές για αυτό το pin χρησιμοποιούνται ήδη" +#: ports/raspberrypi/common-hal/picogame/Display.c py/argcheck.c py/runtime.c +#: shared-bindings/bitmapfilter/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c +msgid "%q must be of type %q, not %q" +msgstr "%q πρέπει να είναι τύπου %q, όχι %q" + #: ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c msgid "Pins must be sequential GPIO pins" msgstr "" @@ -2250,6 +2278,10 @@ msgstr "" msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" msgstr "" +#: ports/zephyr-cp/common-hal/usb_hid/Device.c shared-module/usb_hid/Device.c +msgid "USB error" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -2298,7 +2330,7 @@ msgid "extra keyword arguments given" msgstr "" #: py/argcheck.c shared-bindings/_stage/__init__.c -#: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/digitalio/DigitalInOut.c shared-bindings/picogame/__init__.c msgid "argument num/types mismatch" msgstr "" @@ -2319,12 +2351,6 @@ msgstr "%q πρέπει να είναι >= %d" msgid "%q must be <= %d" msgstr "%q πρέπει να είναι <= %d" -#: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c -#: shared-module/synthio/__init__.c -msgid "%q must be of type %q, not %q" -msgstr "%q πρέπει να είναι τύπου %q, όχι %q" - #: py/argcheck.c msgid "%q length must be %d-%d" msgstr "%q μήκος πρέπει να είναι %d-%d" @@ -2874,7 +2900,9 @@ msgstr "" msgid "schedule queue full" msgstr "" -#: py/modstruct.c shared-module/struct/__init__.c +#: py/modstruct.c shared-bindings/picogame/Bitmap.c +#: shared-bindings/picogame/Canvas.c shared-bindings/picogame/Framebuffer.c +#: shared-module/picogame/__init__.c shared-module/struct/__init__.c msgid "buffer too small" msgstr "" @@ -3745,6 +3773,7 @@ msgid "file must be a file opened in byte mode" msgstr "" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/Flanger.c #: shared-bindings/audiodelays/GranularPitchShift.c #: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c @@ -3762,10 +3791,6 @@ msgstr "" msgid "bits_per_sample must be 16" msgstr "" -#: shared-bindings/audioi2sin/I2SIn.c -msgid "%q requires %q" -msgstr "" - #: shared-bindings/audioi2sin/I2SIn.c #, c-format msgid "invalid destination buffer, must be an array of type: %c" @@ -3775,15 +3800,6 @@ msgstr "" msgid "%q and %q must be different" msgstr "%q και %q πρεπει να είναι διαφορετικά" -#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c -#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c -msgid "Function requires lock" -msgstr "" - -#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/SPI.c -msgid "buffer slices must be of equal length" -msgstr "" - #: shared-bindings/bitmapfilter/__init__.c msgid "" "weights must be a sequence with an odd square number of elements (usually 9 " @@ -4102,6 +4118,11 @@ msgstr "" msgid "Specify exactly one of data0 or data_pins" msgstr "" +#: shared-bindings/picogame/Bitmap.c shared-module/msgpack/__init__.c +#: supervisor/shared/settings.c +msgid "Invalid format" +msgstr "" + #: shared-bindings/ps2io/Ps2.c msgid "Failed sending command." msgstr "" @@ -4198,6 +4219,10 @@ msgstr "" msgid "timestamp out of range for platform time_t" msgstr "" +#: shared-bindings/traceback/__init__.c +msgid "Supply both or neither of %q and %q" +msgstr "" + #: shared-bindings/traceback/__init__.c msgid "file write is not available" msgstr "" @@ -4422,10 +4447,6 @@ msgstr "" msgid "no default packer" msgstr "" -#: shared-module/msgpack/__init__.c supervisor/shared/settings.c -msgid "Invalid format" -msgstr "" - #: shared-module/paralleldisplaybus/ParallelBus.c msgid "" "This microcontroller only supports data0=, not data_pins=, because it " @@ -4529,10 +4550,6 @@ msgstr "" msgid "USB busy" msgstr "" -#: shared-module/usb_hid/Device.c -msgid "USB error" -msgstr "" - #: shared-module/vectorio/Circle.c shared-module/vectorio/Polygon.c #: shared-module/vectorio/Rectangle.c msgid "can only have one parent" diff --git a/locale/hi.po b/locale/hi.po index 483002dfde2..68b041e16a5 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -1043,7 +1043,7 @@ msgstr "" #: ports/espressif/common-hal/sdioio/SDCard.c #: ports/raspberrypi/common-hal/sdioio/SDCard.c #: ports/stm/common-hal/sdioio/SDCard.c shared-bindings/floppyio/__init__.c -#: shared-module/sdcardio/SDCard.c +#: shared-bindings/picogame/Canvas.c shared-module/sdcardio/SDCard.c #, c-format msgid "Buffer must be a multiple of %d bytes" msgstr "" @@ -1202,6 +1202,7 @@ msgstr "" #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c +#: ports/zephyr-cp/common-hal/_bleio/Characteristic.c msgid "No CCCD for this Characteristic" msgstr "" @@ -1224,11 +1225,13 @@ msgstr "" #: ports/espressif/common-hal/_bleio/PacketBuffer.c #: ports/nordic/common-hal/_bleio/PacketBuffer.c +#: ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c msgid "Writes not supported on Characteristic" msgstr "" #: ports/espressif/common-hal/_bleio/PacketBuffer.c #: ports/nordic/common-hal/_bleio/PacketBuffer.c +#: ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c msgid "Total data to write is larger than %q" msgstr "" @@ -1243,6 +1246,9 @@ msgstr "" #: ports/espressif/common-hal/_bleio/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Characteristic.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c +#: ports/zephyr-cp/common-hal/_bleio/Descriptor.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" msgstr "" @@ -1472,6 +1478,7 @@ msgid "Requested resource not found" msgstr "" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/raspberrypi/common-hal/picogame/Display.c msgid "Operation or feature not supported" msgstr "" @@ -1678,7 +1685,8 @@ msgstr "" msgid "Only IPv4 addresses supported" msgstr "" -#: ports/mimxrt10xx/common-hal/busio/SPI.c shared-bindings/busio/SPI.c +#: ports/mimxrt10xx/common-hal/busio/SPI.c +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/busio/SPI.c msgid "Must provide MISO or MOSI pin" msgstr "" @@ -1734,12 +1742,14 @@ msgid "Unknown gatt error: 0x%04x" msgstr "" #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c msgid "" -"Unspecified issue. Can be that the pairing prompt on the other device was " +"Unspecified issue. The pairing request on the other device may have been " "declined or ignored." msgstr "" #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c #, c-format msgid "Unknown security error: 0x%04x" msgstr "" @@ -1809,9 +1819,9 @@ msgstr "" #: ports/raspberrypi/bindings/cyw43/__init__.c py/argcheck.c py/objexcept.c #: shared-bindings/bitmapfilter/__init__.c shared-bindings/canio/CAN.c -#: shared-bindings/digitalio/Pull.c shared-bindings/supervisor/__init__.c -#: shared-module/audiofilters/Filter.c shared-module/displayio/__init__.c -#: shared-module/synthio/Synthesizer.c +#: shared-bindings/digitalio/Pull.c shared-bindings/picogame/Sprite.c +#: shared-bindings/supervisor/__init__.c shared-module/audiofilters/Filter.c +#: shared-module/displayio/__init__.c shared-module/synthio/Synthesizer.c msgid "%q must be of type %q or %q, not %q" msgstr "" @@ -1840,6 +1850,17 @@ msgstr "" msgid "In-buffer elements must be <= 4 bytes long" msgstr "" +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/bitbangio/I2C.c +#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c +#: shared-bindings/busio/SPI.c +msgid "Function requires lock" +msgstr "" + +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/bitbangio/SPI.c +#: shared-bindings/busio/SPI.c +msgid "buffer slices must be of equal length" +msgstr "" + #: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" @@ -1954,6 +1975,13 @@ msgstr "" msgid "All timers for this pin are in use" msgstr "" +#: ports/raspberrypi/common-hal/picogame/Display.c py/argcheck.c py/runtime.c +#: shared-bindings/bitmapfilter/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c +msgid "%q must be of type %q, not %q" +msgstr "" + #: ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c msgid "Pins must be sequential GPIO pins" msgstr "" @@ -2240,6 +2268,10 @@ msgstr "" msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" msgstr "" +#: ports/zephyr-cp/common-hal/usb_hid/Device.c shared-module/usb_hid/Device.c +msgid "USB error" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -2288,7 +2320,7 @@ msgid "extra keyword arguments given" msgstr "" #: py/argcheck.c shared-bindings/_stage/__init__.c -#: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/digitalio/DigitalInOut.c shared-bindings/picogame/__init__.c msgid "argument num/types mismatch" msgstr "" @@ -2309,12 +2341,6 @@ msgstr "" msgid "%q must be <= %d" msgstr "" -#: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c -#: shared-module/synthio/__init__.c -msgid "%q must be of type %q, not %q" -msgstr "" - #: py/argcheck.c msgid "%q length must be %d-%d" msgstr "" @@ -2864,7 +2890,9 @@ msgstr "" msgid "schedule queue full" msgstr "" -#: py/modstruct.c shared-module/struct/__init__.c +#: py/modstruct.c shared-bindings/picogame/Bitmap.c +#: shared-bindings/picogame/Canvas.c shared-bindings/picogame/Framebuffer.c +#: shared-module/picogame/__init__.c shared-module/struct/__init__.c msgid "buffer too small" msgstr "" @@ -3728,6 +3756,7 @@ msgid "file must be a file opened in byte mode" msgstr "" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/Flanger.c #: shared-bindings/audiodelays/GranularPitchShift.c #: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c @@ -3745,10 +3774,6 @@ msgstr "" msgid "bits_per_sample must be 16" msgstr "" -#: shared-bindings/audioi2sin/I2SIn.c -msgid "%q requires %q" -msgstr "" - #: shared-bindings/audioi2sin/I2SIn.c #, c-format msgid "invalid destination buffer, must be an array of type: %c" @@ -3758,15 +3783,6 @@ msgstr "" msgid "%q and %q must be different" msgstr "" -#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c -#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c -msgid "Function requires lock" -msgstr "" - -#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/SPI.c -msgid "buffer slices must be of equal length" -msgstr "" - #: shared-bindings/bitmapfilter/__init__.c msgid "" "weights must be a sequence with an odd square number of elements (usually 9 " @@ -4084,6 +4100,11 @@ msgstr "" msgid "Specify exactly one of data0 or data_pins" msgstr "" +#: shared-bindings/picogame/Bitmap.c shared-module/msgpack/__init__.c +#: supervisor/shared/settings.c +msgid "Invalid format" +msgstr "" + #: shared-bindings/ps2io/Ps2.c msgid "Failed sending command." msgstr "" @@ -4179,6 +4200,10 @@ msgstr "" msgid "timestamp out of range for platform time_t" msgstr "" +#: shared-bindings/traceback/__init__.c +msgid "Supply both or neither of %q and %q" +msgstr "" + #: shared-bindings/traceback/__init__.c msgid "file write is not available" msgstr "" @@ -4403,10 +4428,6 @@ msgstr "" msgid "no default packer" msgstr "" -#: shared-module/msgpack/__init__.c supervisor/shared/settings.c -msgid "Invalid format" -msgstr "" - #: shared-module/paralleldisplaybus/ParallelBus.c msgid "" "This microcontroller only supports data0=, not data_pins=, because it " @@ -4510,10 +4531,6 @@ msgstr "" msgid "USB busy" msgstr "" -#: shared-module/usb_hid/Device.c -msgid "USB error" -msgstr "" - #: shared-module/vectorio/Circle.c shared-module/vectorio/Polygon.c #: shared-module/vectorio/Rectangle.c msgid "can only have one parent" diff --git a/locale/ko.po b/locale/ko.po index 22f39814a90..0811406c436 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -1053,7 +1053,7 @@ msgstr "%q 실패: %d" #: ports/espressif/common-hal/sdioio/SDCard.c #: ports/raspberrypi/common-hal/sdioio/SDCard.c #: ports/stm/common-hal/sdioio/SDCard.c shared-bindings/floppyio/__init__.c -#: shared-module/sdcardio/SDCard.c +#: shared-bindings/picogame/Canvas.c shared-module/sdcardio/SDCard.c #, c-format msgid "Buffer must be a multiple of %d bytes" msgstr "" @@ -1215,6 +1215,7 @@ msgstr "" #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c +#: ports/zephyr-cp/common-hal/_bleio/Characteristic.c msgid "No CCCD for this Characteristic" msgstr "이 특성에 대한 CCCD가 없습니다" @@ -1237,11 +1238,13 @@ msgstr "" #: ports/espressif/common-hal/_bleio/PacketBuffer.c #: ports/nordic/common-hal/_bleio/PacketBuffer.c +#: ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c msgid "Writes not supported on Characteristic" msgstr "" #: ports/espressif/common-hal/_bleio/PacketBuffer.c #: ports/nordic/common-hal/_bleio/PacketBuffer.c +#: ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c msgid "Total data to write is larger than %q" msgstr "" @@ -1257,6 +1260,9 @@ msgstr "잘못된 BLE 파라미터" #: ports/espressif/common-hal/_bleio/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Characteristic.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c +#: ports/zephyr-cp/common-hal/_bleio/Descriptor.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" msgstr "연결되지 않았습니다" @@ -1491,6 +1497,7 @@ msgid "Requested resource not found" msgstr "" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/raspberrypi/common-hal/picogame/Display.c msgid "Operation or feature not supported" msgstr "작업 또는 기능이 지원되지 않습니다" @@ -1701,7 +1708,8 @@ msgstr "Wifi 메모리 할당에 실패했습니다" msgid "Only IPv4 addresses supported" msgstr "IPv4 주소만 지원됩니다" -#: ports/mimxrt10xx/common-hal/busio/SPI.c shared-bindings/busio/SPI.c +#: ports/mimxrt10xx/common-hal/busio/SPI.c +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/busio/SPI.c msgid "Must provide MISO or MOSI pin" msgstr "MISO 또는 MOSI 핀을 제공해야 합니다" @@ -1759,12 +1767,14 @@ msgid "Unknown gatt error: 0x%04x" msgstr "" #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c msgid "" -"Unspecified issue. Can be that the pairing prompt on the other device was " +"Unspecified issue. The pairing request on the other device may have been " "declined or ignored." msgstr "" #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c #, c-format msgid "Unknown security error: 0x%04x" msgstr "" @@ -1835,9 +1845,9 @@ msgstr "오디오 변환이 구현되지 않음" #: ports/raspberrypi/bindings/cyw43/__init__.c py/argcheck.c py/objexcept.c #: shared-bindings/bitmapfilter/__init__.c shared-bindings/canio/CAN.c -#: shared-bindings/digitalio/Pull.c shared-bindings/supervisor/__init__.c -#: shared-module/audiofilters/Filter.c shared-module/displayio/__init__.c -#: shared-module/synthio/Synthesizer.c +#: shared-bindings/digitalio/Pull.c shared-bindings/picogame/Sprite.c +#: shared-bindings/supervisor/__init__.c shared-module/audiofilters/Filter.c +#: shared-module/displayio/__init__.c shared-module/synthio/Synthesizer.c msgid "%q must be of type %q or %q, not %q" msgstr "%q는 %q가 아닌 %q 또는 %q 유형이어야 합니다" @@ -1866,6 +1876,17 @@ msgstr "출력 버퍼 요소의 길이는 <= 4 바이트 여야 합니다" msgid "In-buffer elements must be <= 4 bytes long" msgstr "버퍼 내 요소 길이는 <= 4여야 합니다" +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/bitbangio/I2C.c +#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c +#: shared-bindings/busio/SPI.c +msgid "Function requires lock" +msgstr "이 함수에는 잠금이 필요합니다" + +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/bitbangio/SPI.c +#: shared-bindings/busio/SPI.c +msgid "buffer slices must be of equal length" +msgstr "" + #: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" @@ -1983,6 +2004,13 @@ msgstr "" msgid "All timers for this pin are in use" msgstr "핀의 모든 타이머가 사용 중입니다" +#: ports/raspberrypi/common-hal/picogame/Display.c py/argcheck.c py/runtime.c +#: shared-bindings/bitmapfilter/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c +msgid "%q must be of type %q, not %q" +msgstr "%q는 %q가 아니라 %q 유형이어야 합니다" + #: ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c msgid "Pins must be sequential GPIO pins" msgstr "핀은 순차적인 GPIO 핀이어야 합니다" @@ -2281,6 +2309,10 @@ msgstr "" msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" msgstr "" +#: ports/zephyr-cp/common-hal/usb_hid/Device.c shared-module/usb_hid/Device.c +msgid "USB error" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -2329,7 +2361,7 @@ msgid "extra keyword arguments given" msgstr "" #: py/argcheck.c shared-bindings/_stage/__init__.c -#: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/digitalio/DigitalInOut.c shared-bindings/picogame/__init__.c msgid "argument num/types mismatch" msgstr "" @@ -2352,12 +2384,6 @@ msgstr "%q 는 >= %d 여야 합니다" msgid "%q must be <= %d" msgstr "%q 는 <= %d 여야 합니다" -#: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c -#: shared-module/synthio/__init__.c -msgid "%q must be of type %q, not %q" -msgstr "%q는 %q가 아니라 %q 유형이어야 합니다" - #: py/argcheck.c msgid "%q length must be %d-%d" msgstr "%q 길이는 %d - %d이어야 합니다" @@ -2913,7 +2939,9 @@ msgstr "" msgid "schedule queue full" msgstr "" -#: py/modstruct.c shared-module/struct/__init__.c +#: py/modstruct.c shared-bindings/picogame/Bitmap.c +#: shared-bindings/picogame/Canvas.c shared-bindings/picogame/Framebuffer.c +#: shared-module/picogame/__init__.c shared-module/struct/__init__.c msgid "buffer too small" msgstr "" @@ -3784,6 +3812,7 @@ msgid "file must be a file opened in byte mode" msgstr "" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/Flanger.c #: shared-bindings/audiodelays/GranularPitchShift.c #: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c @@ -3801,10 +3830,6 @@ msgstr "" msgid "bits_per_sample must be 16" msgstr "" -#: shared-bindings/audioi2sin/I2SIn.c -msgid "%q requires %q" -msgstr "" - #: shared-bindings/audioi2sin/I2SIn.c #, c-format msgid "invalid destination buffer, must be an array of type: %c" @@ -3814,15 +3839,6 @@ msgstr "" msgid "%q and %q must be different" msgstr "%q와 %q는 달라야 합니다" -#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c -#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c -msgid "Function requires lock" -msgstr "이 함수에는 잠금이 필요합니다" - -#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/SPI.c -msgid "buffer slices must be of equal length" -msgstr "" - #: shared-bindings/bitmapfilter/__init__.c msgid "" "weights must be a sequence with an odd square number of elements (usually 9 " @@ -4143,6 +4159,11 @@ msgstr "임의의 하드웨어를 사용할 수 없습니다" msgid "Specify exactly one of data0 or data_pins" msgstr "" +#: shared-bindings/picogame/Bitmap.c shared-module/msgpack/__init__.c +#: supervisor/shared/settings.c +msgid "Invalid format" +msgstr "잘못된 형식" + #: shared-bindings/ps2io/Ps2.c msgid "Failed sending command." msgstr "명령을 보내는 것에 실패했습니다." @@ -4239,6 +4260,10 @@ msgstr "파일을 찾을 수 없습니다" msgid "timestamp out of range for platform time_t" msgstr "" +#: shared-bindings/traceback/__init__.c +msgid "Supply both or neither of %q and %q" +msgstr "" + #: shared-bindings/traceback/__init__.c msgid "file write is not available" msgstr "" @@ -4467,10 +4492,6 @@ msgstr "" msgid "no default packer" msgstr "" -#: shared-module/msgpack/__init__.c supervisor/shared/settings.c -msgid "Invalid format" -msgstr "잘못된 형식" - #: shared-module/paralleldisplaybus/ParallelBus.c msgid "" "This microcontroller only supports data0=, not data_pins=, because it " @@ -4576,10 +4597,6 @@ msgstr "구성이 설정되어 있지 않습니다" msgid "USB busy" msgstr "" -#: shared-module/usb_hid/Device.c -msgid "USB error" -msgstr "" - #: shared-module/vectorio/Circle.c shared-module/vectorio/Polygon.c #: shared-module/vectorio/Rectangle.c msgid "can only have one parent" diff --git a/locale/ru.po b/locale/ru.po index 0e632d919c8..3e8d2d4e634 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -1056,7 +1056,7 @@ msgstr "%q сбой: %d" #: ports/espressif/common-hal/sdioio/SDCard.c #: ports/raspberrypi/common-hal/sdioio/SDCard.c #: ports/stm/common-hal/sdioio/SDCard.c shared-bindings/floppyio/__init__.c -#: shared-module/sdcardio/SDCard.c +#: shared-bindings/picogame/Canvas.c shared-module/sdcardio/SDCard.c #, c-format msgid "Buffer must be a multiple of %d bytes" msgstr "Буфер должен быть кратен %d байт" @@ -1215,6 +1215,7 @@ msgstr "Слишком много дескрипторов" #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c +#: ports/zephyr-cp/common-hal/_bleio/Characteristic.c msgid "No CCCD for this Characteristic" msgstr "Для этой характеристики нет CCCD" @@ -1237,11 +1238,13 @@ msgstr "максимальная_длина должна быть 0-%d когд #: ports/espressif/common-hal/_bleio/PacketBuffer.c #: ports/nordic/common-hal/_bleio/PacketBuffer.c +#: ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c msgid "Writes not supported on Characteristic" msgstr "Запись не поддерживается в Характеристика" #: ports/espressif/common-hal/_bleio/PacketBuffer.c #: ports/nordic/common-hal/_bleio/PacketBuffer.c +#: ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c msgid "Total data to write is larger than %q" msgstr "Общее количество данных для записи превышает %q" @@ -1256,6 +1259,9 @@ msgstr "Недопустимый параметр BLE" #: ports/espressif/common-hal/_bleio/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Characteristic.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c +#: ports/zephyr-cp/common-hal/_bleio/Descriptor.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" msgstr "Не подключено" @@ -1490,6 +1496,7 @@ msgid "Requested resource not found" msgstr "Запрошенный ресурс не найден" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/raspberrypi/common-hal/picogame/Display.c msgid "Operation or feature not supported" msgstr "Операция или функция, не поддерживаемые" @@ -1696,7 +1703,8 @@ msgstr "Не удалось выделить память Wifi" msgid "Only IPv4 addresses supported" msgstr "Поддерживаются только адреса IPv4" -#: ports/mimxrt10xx/common-hal/busio/SPI.c shared-bindings/busio/SPI.c +#: ports/mimxrt10xx/common-hal/busio/SPI.c +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/busio/SPI.c msgid "Must provide MISO or MOSI pin" msgstr "Пин MISO или MOSI должен быть предоставлен" @@ -1754,14 +1762,14 @@ msgid "Unknown gatt error: 0x%04x" msgstr "Неизвестная ошибка gatt: 0x%04x" #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c msgid "" -"Unspecified issue. Can be that the pairing prompt on the other device was " +"Unspecified issue. The pairing request on the other device may have been " "declined or ignored." msgstr "" -"Неуказанная проблема. Возможно, запрос на сопряжение на другом устройстве " -"был отклонен или проигнорирован." #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c #, c-format msgid "Unknown security error: 0x%04x" msgstr "Неизвестная ошибка безопасности: 0x%04x" @@ -1835,9 +1843,9 @@ msgstr "Преобразование звука не реализовано" #: ports/raspberrypi/bindings/cyw43/__init__.c py/argcheck.c py/objexcept.c #: shared-bindings/bitmapfilter/__init__.c shared-bindings/canio/CAN.c -#: shared-bindings/digitalio/Pull.c shared-bindings/supervisor/__init__.c -#: shared-module/audiofilters/Filter.c shared-module/displayio/__init__.c -#: shared-module/synthio/Synthesizer.c +#: shared-bindings/digitalio/Pull.c shared-bindings/picogame/Sprite.c +#: shared-bindings/supervisor/__init__.c shared-module/audiofilters/Filter.c +#: shared-module/displayio/__init__.c shared-module/synthio/Synthesizer.c msgid "%q must be of type %q or %q, not %q" msgstr "%q должно быть типа%q или%q, а не%q" @@ -1866,6 +1874,17 @@ msgstr "Элементы вне буфера должны иметь длину msgid "In-buffer elements must be <= 4 bytes long" msgstr "Элементы буфера должны быть длиной <= 4 байта" +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/bitbangio/I2C.c +#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c +#: shared-bindings/busio/SPI.c +msgid "Function requires lock" +msgstr "Функция требует блокировки" + +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/bitbangio/SPI.c +#: shared-bindings/busio/SPI.c +msgid "buffer slices must be of equal length" +msgstr "Буферные фрагменты должны быть одинаковой длины" + #: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" @@ -1980,6 +1999,13 @@ msgstr "Невозможно записать в постоянную памят msgid "All timers for this pin are in use" msgstr "Все таймеры для этого пина уже используются" +#: ports/raspberrypi/common-hal/picogame/Display.c py/argcheck.c py/runtime.c +#: shared-bindings/bitmapfilter/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c +msgid "%q must be of type %q, not %q" +msgstr "%q должно быть типа %q, а не %q" + #: ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c msgid "Pins must be sequential GPIO pins" msgstr "Пины должны быть последовательными выводами GPIO" @@ -2272,6 +2298,10 @@ msgstr "" msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" msgstr "" +#: ports/zephyr-cp/common-hal/usb_hid/Device.c shared-module/usb_hid/Device.c +msgid "USB error" +msgstr "Ошибка USB" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -2320,7 +2350,7 @@ msgid "extra keyword arguments given" msgstr "Приведены дополнительные аргументы ключевых слов" #: py/argcheck.c shared-bindings/_stage/__init__.c -#: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/digitalio/DigitalInOut.c shared-bindings/picogame/__init__.c msgid "argument num/types mismatch" msgstr "Аргумент Несоответствие числа/типов" @@ -2343,12 +2373,6 @@ msgstr "%q должно быть >= %d" msgid "%q must be <= %d" msgstr "%q должно быть <= %d" -#: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c -#: shared-module/synthio/__init__.c -msgid "%q must be of type %q, not %q" -msgstr "%q должно быть типа %q, а не %q" - #: py/argcheck.c msgid "%q length must be %d-%d" msgstr "Длинна %q должна быть %d-%d" @@ -2905,7 +2929,9 @@ msgstr "отрицательный факториал" msgid "schedule queue full" msgstr "Расписание Очередь заполнена" -#: py/modstruct.c shared-module/struct/__init__.c +#: py/modstruct.c shared-bindings/picogame/Bitmap.c +#: shared-bindings/picogame/Canvas.c shared-bindings/picogame/Framebuffer.c +#: shared-module/picogame/__init__.c shared-module/struct/__init__.c msgid "buffer too small" msgstr "буфер слишком мал" @@ -3787,6 +3813,7 @@ msgid "file must be a file opened in byte mode" msgstr "Файл должен быть файлом, открытым в байтовом режиме" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/Flanger.c #: shared-bindings/audiodelays/GranularPitchShift.c #: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c @@ -3804,10 +3831,6 @@ msgstr "" msgid "bits_per_sample must be 16" msgstr "" -#: shared-bindings/audioi2sin/I2SIn.c -msgid "%q requires %q" -msgstr "" - #: shared-bindings/audioi2sin/I2SIn.c #, c-format msgid "invalid destination buffer, must be an array of type: %c" @@ -3817,15 +3840,6 @@ msgstr "" msgid "%q and %q must be different" msgstr "%q и %q должны быть разными" -#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c -#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c -msgid "Function requires lock" -msgstr "Функция требует блокировки" - -#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/SPI.c -msgid "buffer slices must be of equal length" -msgstr "Буферные фрагменты должны быть одинаковой длины" - #: shared-bindings/bitmapfilter/__init__.c msgid "" "weights must be a sequence with an odd square number of elements (usually 9 " @@ -4157,6 +4171,11 @@ msgstr "Отсутствует аппаратный генератор случ msgid "Specify exactly one of data0 or data_pins" msgstr "Укажите точно один из data0 или data_pins" +#: shared-bindings/picogame/Bitmap.c shared-module/msgpack/__init__.c +#: supervisor/shared/settings.c +msgid "Invalid format" +msgstr "Недопустимый формат" + #: shared-bindings/ps2io/Ps2.c msgid "Failed sending command." msgstr "Не удалось отправить команду." @@ -4257,6 +4276,10 @@ msgid "timestamp out of range for platform time_t" msgstr "" "Временная метка выходит за пределы допустимого диапазона для платформы time_t" +#: shared-bindings/traceback/__init__.c +msgid "Supply both or neither of %q and %q" +msgstr "" + #: shared-bindings/traceback/__init__.c msgid "file write is not available" msgstr "Запись файлов недоступна" @@ -4485,10 +4508,6 @@ msgstr "короткое чтение" msgid "no default packer" msgstr "Нет упаковщика по умолчанию" -#: shared-module/msgpack/__init__.c supervisor/shared/settings.c -msgid "Invalid format" -msgstr "Недопустимый формат" - #: shared-module/paralleldisplaybus/ParallelBus.c msgid "" "This microcontroller only supports data0=, not data_pins=, because it " @@ -4594,10 +4613,6 @@ msgstr "Нет конфигураций" msgid "USB busy" msgstr "USB занят" -#: shared-module/usb_hid/Device.c -msgid "USB error" -msgstr "Ошибка USB" - #: shared-module/vectorio/Circle.c shared-module/vectorio/Polygon.c #: shared-module/vectorio/Rectangle.c msgid "can only have one parent" @@ -4748,6 +4763,13 @@ msgstr "выключить" msgid "No IP" msgstr "Нет IP" +#~ msgid "" +#~ "Unspecified issue. Can be that the pairing prompt on the other device was " +#~ "declined or ignored." +#~ msgstr "" +#~ "Неуказанная проблема. Возможно, запрос на сопряжение на другом устройстве " +#~ "был отклонен или проигнорирован." + #, c-format #~ msgid "SDIO Init Error %x" #~ msgstr "Ошибка инициализации SDIO %x" diff --git a/locale/tr.po b/locale/tr.po index d04687334d2..41505586489 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -1052,7 +1052,7 @@ msgstr "%q hata: %d" #: ports/espressif/common-hal/sdioio/SDCard.c #: ports/raspberrypi/common-hal/sdioio/SDCard.c #: ports/stm/common-hal/sdioio/SDCard.c shared-bindings/floppyio/__init__.c -#: shared-module/sdcardio/SDCard.c +#: shared-bindings/picogame/Canvas.c shared-module/sdcardio/SDCard.c #, c-format msgid "Buffer must be a multiple of %d bytes" msgstr "Tampon, %d baytların katı olmalıdır" @@ -1212,6 +1212,7 @@ msgstr "" #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c +#: ports/zephyr-cp/common-hal/_bleio/Characteristic.c msgid "No CCCD for this Characteristic" msgstr "" @@ -1234,11 +1235,13 @@ msgstr "" #: ports/espressif/common-hal/_bleio/PacketBuffer.c #: ports/nordic/common-hal/_bleio/PacketBuffer.c +#: ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c msgid "Writes not supported on Characteristic" msgstr "" #: ports/espressif/common-hal/_bleio/PacketBuffer.c #: ports/nordic/common-hal/_bleio/PacketBuffer.c +#: ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c msgid "Total data to write is larger than %q" msgstr "" @@ -1253,6 +1256,9 @@ msgstr "Geçersiz BLE parametresi" #: ports/espressif/common-hal/_bleio/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Characteristic.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c +#: ports/zephyr-cp/common-hal/_bleio/Descriptor.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" msgstr "" @@ -1484,6 +1490,7 @@ msgid "Requested resource not found" msgstr "" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/raspberrypi/common-hal/picogame/Display.c msgid "Operation or feature not supported" msgstr "" @@ -1692,7 +1699,8 @@ msgstr "" msgid "Only IPv4 addresses supported" msgstr "" -#: ports/mimxrt10xx/common-hal/busio/SPI.c shared-bindings/busio/SPI.c +#: ports/mimxrt10xx/common-hal/busio/SPI.c +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/busio/SPI.c msgid "Must provide MISO or MOSI pin" msgstr "" @@ -1750,12 +1758,14 @@ msgid "Unknown gatt error: 0x%04x" msgstr "" #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c msgid "" -"Unspecified issue. Can be that the pairing prompt on the other device was " +"Unspecified issue. The pairing request on the other device may have been " "declined or ignored." msgstr "" #: ports/nordic/common-hal/_bleio/__init__.c +#: ports/zephyr-cp/common-hal/_bleio/Connection.c #, c-format msgid "Unknown security error: 0x%04x" msgstr "" @@ -1825,9 +1835,9 @@ msgstr "Ses dönüşümü implemente edilmedi" #: ports/raspberrypi/bindings/cyw43/__init__.c py/argcheck.c py/objexcept.c #: shared-bindings/bitmapfilter/__init__.c shared-bindings/canio/CAN.c -#: shared-bindings/digitalio/Pull.c shared-bindings/supervisor/__init__.c -#: shared-module/audiofilters/Filter.c shared-module/displayio/__init__.c -#: shared-module/synthio/Synthesizer.c +#: shared-bindings/digitalio/Pull.c shared-bindings/picogame/Sprite.c +#: shared-bindings/supervisor/__init__.c shared-module/audiofilters/Filter.c +#: shared-module/displayio/__init__.c shared-module/synthio/Synthesizer.c msgid "%q must be of type %q or %q, not %q" msgstr "%q; %q veya %q tipi olmalıdır, %q değil" @@ -1856,6 +1866,17 @@ msgstr "" msgid "In-buffer elements must be <= 4 bytes long" msgstr "Buffer öğeleri <=4 bayt uzunluğunda olmalı" +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/bitbangio/I2C.c +#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c +#: shared-bindings/busio/SPI.c +msgid "Function requires lock" +msgstr "Fonksiyon kilit gerektirir" + +#: ports/raspberrypi/bindings/wiznet/PIO_SPI.c shared-bindings/bitbangio/SPI.c +#: shared-bindings/busio/SPI.c +msgid "buffer slices must be of equal length" +msgstr "" + #: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" @@ -1970,6 +1991,13 @@ msgstr "" msgid "All timers for this pin are in use" msgstr "Bu pin için tüm zamanlayıcılar kullanımda" +#: ports/raspberrypi/common-hal/picogame/Display.c py/argcheck.c py/runtime.c +#: shared-bindings/bitmapfilter/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c +msgid "%q must be of type %q, not %q" +msgstr "%q; %q tipi olmalıdır, %q değil" + #: ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c msgid "Pins must be sequential GPIO pins" msgstr "" @@ -2257,6 +2285,10 @@ msgstr "" msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" msgstr "" +#: ports/zephyr-cp/common-hal/usb_hid/Device.c shared-module/usb_hid/Device.c +msgid "USB error" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -2305,7 +2337,7 @@ msgid "extra keyword arguments given" msgstr "" #: py/argcheck.c shared-bindings/_stage/__init__.c -#: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/digitalio/DigitalInOut.c shared-bindings/picogame/__init__.c msgid "argument num/types mismatch" msgstr "" @@ -2326,12 +2358,6 @@ msgstr "%q >= %d olmalıdır" msgid "%q must be <= %d" msgstr "%q <= %d olmalıdır" -#: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c -#: shared-module/synthio/__init__.c -msgid "%q must be of type %q, not %q" -msgstr "%q; %q tipi olmalıdır, %q değil" - #: py/argcheck.c msgid "%q length must be %d-%d" msgstr "%q boyutları %d-%d olmalıdır" @@ -2881,7 +2907,9 @@ msgstr "" msgid "schedule queue full" msgstr "" -#: py/modstruct.c shared-module/struct/__init__.c +#: py/modstruct.c shared-bindings/picogame/Bitmap.c +#: shared-bindings/picogame/Canvas.c shared-bindings/picogame/Framebuffer.c +#: shared-module/picogame/__init__.c shared-module/struct/__init__.c msgid "buffer too small" msgstr "" @@ -3746,6 +3774,7 @@ msgid "file must be a file opened in byte mode" msgstr "" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/Flanger.c #: shared-bindings/audiodelays/GranularPitchShift.c #: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c @@ -3763,10 +3792,6 @@ msgstr "" msgid "bits_per_sample must be 16" msgstr "" -#: shared-bindings/audioi2sin/I2SIn.c -msgid "%q requires %q" -msgstr "" - #: shared-bindings/audioi2sin/I2SIn.c #, c-format msgid "invalid destination buffer, must be an array of type: %c" @@ -3776,15 +3801,6 @@ msgstr "" msgid "%q and %q must be different" msgstr "%q ve %q farklı olmalılar" -#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c -#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c -msgid "Function requires lock" -msgstr "Fonksiyon kilit gerektirir" - -#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/SPI.c -msgid "buffer slices must be of equal length" -msgstr "" - #: shared-bindings/bitmapfilter/__init__.c msgid "" "weights must be a sequence with an odd square number of elements (usually 9 " @@ -4104,6 +4120,11 @@ msgstr "" msgid "Specify exactly one of data0 or data_pins" msgstr "" +#: shared-bindings/picogame/Bitmap.c shared-module/msgpack/__init__.c +#: supervisor/shared/settings.c +msgid "Invalid format" +msgstr "" + #: shared-bindings/ps2io/Ps2.c msgid "Failed sending command." msgstr "Komut gönderilemedi." @@ -4201,6 +4222,10 @@ msgstr "" msgid "timestamp out of range for platform time_t" msgstr "" +#: shared-bindings/traceback/__init__.c +msgid "Supply both or neither of %q and %q" +msgstr "" + #: shared-bindings/traceback/__init__.c msgid "file write is not available" msgstr "" @@ -4426,10 +4451,6 @@ msgstr "" msgid "no default packer" msgstr "" -#: shared-module/msgpack/__init__.c supervisor/shared/settings.c -msgid "Invalid format" -msgstr "" - #: shared-module/paralleldisplaybus/ParallelBus.c msgid "" "This microcontroller only supports data0=, not data_pins=, because it " @@ -4533,10 +4554,6 @@ msgstr "" msgid "USB busy" msgstr "" -#: shared-module/usb_hid/Device.c -msgid "USB error" -msgstr "" - #: shared-module/vectorio/Circle.c shared-module/vectorio/Polygon.c #: shared-module/vectorio/Rectangle.c msgid "can only have one parent" diff --git a/main.c b/main.c index 2020cb8c307..845d55ca21e 100644 --- a/main.c +++ b/main.c @@ -121,12 +121,6 @@ uint8_t value_out = 0; #include "supervisor/shared/settings.h" #endif -static void reset_devices(void) { - #if CIRCUITPY_BLEIO_HCI - bleio_reset(); - #endif -} - static uint8_t *_heap; static uint8_t *_pystack; static volatile bool _vm_is_running = false; @@ -374,9 +368,6 @@ static void cleanup_after_vm(mp_obj_t exception) { } } - // Reset port-independent devices, like CIRCUITPY_BLEIO_HCI. - reset_devices(); - #if CIRCUITPY_ATEXIT atexit_reset(); #endif @@ -390,7 +381,7 @@ static void cleanup_after_vm(mp_obj_t exception) { memorymonitor_reset(); #endif - // Disable user related BLE state that uses the micropython heap. + // Disable user related BLE state that uses the VM heap. Leave BLE workflow running if it's in use. #if CIRCUITPY_BLEIO bleio_user_reset(); #endif @@ -775,8 +766,10 @@ static bool __attribute__((noinline)) run_code_py(safe_mode_t safe_mode, bool *s // Done waiting, start the board back up. - // We delay resetting BLE until after the wait in case we're transferring - // more files over. + // Resetting BLE is delayed until here, after the wait, in case files were being + // transferred over the BLE workflow during the wait. The reset restarts the BLE + // stack, dropping the workflow connection, only if user code created GATT + // services; otherwise it is a no-op and the connection continues. #if CIRCUITPY_BLEIO bleio_reset(); #endif @@ -992,8 +985,9 @@ static int run_repl(safe_mode_t safe_mode) { #endif cleanup_after_vm(MP_OBJ_SENTINEL); - // Also reset bleio. The above call omits it in case workflows should continue. In this case, - // we're switching straight to another VM so we want to reset. + // Also reset bleio, which cleanup_after_vm() above omits so workflows can + // continue between VMs. As in run_code_py(), this restarts the BLE stack only if + // user code created GATT services. #if CIRCUITPY_BLEIO bleio_reset(); #endif @@ -1085,8 +1079,6 @@ int __attribute__((used)) main(void) { // Reset everything and prep MicroPython to run boot.py. reset_port(); - // Port-independent devices, like CIRCUITPY_BLEIO_HCI. - reset_devices(); reset_board(); // displays init after filesystem, since they could share the flash SPI diff --git a/ports/atmel-samd/boards/pybadge/mpconfigboard.mk b/ports/atmel-samd/boards/pybadge/mpconfigboard.mk index 70571b6c77d..86ce2d47e8c 100644 --- a/ports/atmel-samd/boards/pybadge/mpconfigboard.mk +++ b/ports/atmel-samd/boards/pybadge/mpconfigboard.mk @@ -14,6 +14,7 @@ CIRCUITPY_AESIO = 0 CIRCUITPY_FLOPPYIO = 0 CIRCUITPY_FRAMEBUFFERIO = 0 CIRCUITPY_GIFIO = 0 +CIRCUITPY_EPAPERDISPLAY = 0 CIRCUITPY_I2CDISPLAYBUS = 0 CIRCUITPY_JPEGIO = 0 CIRCUITPY_KEYPAD = 1 diff --git a/ports/cxd56/common-hal/os/__init__.c b/ports/cxd56/common-hal/os/__init__.c index e1024108b07..902228f1db0 100644 --- a/ports/cxd56/common-hal/os/__init__.c +++ b/ports/cxd56/common-hal/os/__init__.c @@ -10,17 +10,7 @@ #include "py/objstr.h" #include "py/objtuple.h" +// No HW TRNG. bool common_hal_os_urandom(uint8_t *buffer, mp_uint_t length) { - uint32_t i = 0; - - while (i < length) { - uint32_t new_random = rand(); - for (int j = 0; j < 4 && i < length; j++) { - buffer[i] = new_random & 0xff; - i++; - new_random >>= 8; - } - } - - return true; + return false; } diff --git a/ports/espressif/boards/adafruit_esp32s3_camera/mpconfigboard.h b/ports/espressif/boards/adafruit_esp32s3_camera/mpconfigboard.h index 25d08535dd6..771a3275fb4 100644 --- a/ports/espressif/boards/adafruit_esp32s3_camera/mpconfigboard.h +++ b/ports/espressif/boards/adafruit_esp32s3_camera/mpconfigboard.h @@ -20,3 +20,7 @@ #define DEFAULT_SPI_BUS_MISO (&pin_GPIO37) #define DOUBLE_TAP_PIN (&pin_GPIO42) + +// Turn off presenting SD card to USB by default. Changes settings.toml default for this setting. +// Workaround for https://github.com/adafruit/circuitpython/issues/11109. +#define CIRCUITPY_SDCARD_USB (false) diff --git a/ports/espressif/boards/espressif_esp32s3_devkitc_1_n32r16/board.c b/ports/espressif/boards/espressif_esp32s3_devkitc_1_n32r16/board.c new file mode 100644 index 00000000000..a3a9eec0471 --- /dev/null +++ b/ports/espressif/boards/espressif_esp32s3_devkitc_1_n32r16/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/espressif_esp32s3_devkitc_1_n32r16/mpconfigboard.h b/ports/espressif/boards/espressif_esp32s3_devkitc_1_n32r16/mpconfigboard.h new file mode 100644 index 00000000000..9df0ebcdb40 --- /dev/null +++ b/ports/espressif/boards/espressif_esp32s3_devkitc_1_n32r16/mpconfigboard.h @@ -0,0 +1,21 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "ESP32-S3-DevKitC-1-N32R16" +#define MICROPY_HW_MCU_NAME "ESP32S3" + +#define MICROPY_HW_NEOPIXEL (&pin_GPIO38) + +#define CIRCUITPY_BOOT_BUTTON (&pin_GPIO0) + +#define DEFAULT_UART_BUS_RX (&pin_GPIO44) +#define DEFAULT_UART_BUS_TX (&pin_GPIO43) + +#define AUTORESET_DELAY_MS 500 diff --git a/ports/espressif/boards/espressif_esp32s3_devkitc_1_n32r16/mpconfigboard.mk b/ports/espressif/boards/espressif_esp32s3_devkitc_1_n32r16/mpconfigboard.mk new file mode 100644 index 00000000000..15fcf8697fc --- /dev/null +++ b/ports/espressif/boards/espressif_esp32s3_devkitc_1_n32r16/mpconfigboard.mk @@ -0,0 +1,14 @@ +USB_VID = 0x303A +USB_PID = 0x7003 +USB_PRODUCT = "ESP32-S3-DevKitC-1-N32R16" +USB_MANUFACTURER = "Espressif" + +IDF_TARGET = esp32s3 + +CIRCUITPY_ESP_FLASH_SIZE = 32MB +CIRCUITPY_ESP_FLASH_MODE = opi +CIRCUITPY_ESP_FLASH_FREQ = 80m + +CIRCUITPY_ESP_PSRAM_SIZE = 16MB +CIRCUITPY_ESP_PSRAM_MODE = opi +CIRCUITPY_ESP_PSRAM_FREQ = 80m diff --git a/ports/espressif/boards/espressif_esp32s3_devkitc_1_n32r16/pins.c b/ports/espressif/boards/espressif_esp32s3_devkitc_1_n32r16/pins.c new file mode 100644 index 00000000000..e77a3813e94 --- /dev/null +++ b/ports/espressif/boards/espressif_esp32s3_devkitc_1_n32r16/pins.c @@ -0,0 +1,55 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_IO0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_IO2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_IO4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_IO5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_IO6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_IO7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_IO8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_IO9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_IO10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_IO11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_IO12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_IO13), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_IO14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_IO15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_IO16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_IO17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_IO18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_IO19), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_IO20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_IO35), MP_ROM_PTR(&pin_GPIO35) }, + { MP_ROM_QSTR(MP_QSTR_IO36), MP_ROM_PTR(&pin_GPIO36) }, + { MP_ROM_QSTR(MP_QSTR_IO37), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_IO38), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_IO39), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_IO40), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_IO41), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_IO42), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_IO43), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_IO44), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_IO45), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_IO46), MP_ROM_PTR(&pin_GPIO46) }, + { MP_ROM_QSTR(MP_QSTR_IO47), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_IO48), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO38) }, + + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/espressif_esp32s3_devkitc_1_n32r16/sdkconfig b/ports/espressif/boards/espressif_esp32s3_devkitc_1_n32r16/sdkconfig new file mode 100644 index 00000000000..e9628662160 --- /dev/null +++ b/ports/espressif/boards/espressif_esp32s3_devkitc_1_n32r16/sdkconfig @@ -0,0 +1,14 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +# end of LWIP + +# end of Component config + +# end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/boards/seeed_xiao_esp32_s3_trmnl_diy_kit/board.c b/ports/espressif/boards/seeed_xiao_esp32_s3_trmnl_diy_kit/board.c new file mode 100644 index 00000000000..fea7fe2c300 --- /dev/null +++ b/ports/espressif/boards/seeed_xiao_esp32_s3_trmnl_diy_kit/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "mpconfigboard.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/seeed_xiao_esp32_s3_trmnl_diy_kit/mpconfigboard.h b/ports/espressif/boards/seeed_xiao_esp32_s3_trmnl_diy_kit/mpconfigboard.h new file mode 100644 index 00000000000..79deafd6125 --- /dev/null +++ b/ports/espressif/boards/seeed_xiao_esp32_s3_trmnl_diy_kit/mpconfigboard.h @@ -0,0 +1,24 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Bernhard Bablok +// +// SPDX-License-Identifier: MIT + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "Seeed Xiao ESP32-S3 Trmnl DIY Kit" +#define MICROPY_HW_MCU_NAME "ESP32S3" + +#define MICROPY_HW_LED_STATUS (&pin_GPIO21) + +// the unpopulated NFC-header also provides UART +#define CIRCUITPY_BOARD_UART (1) +#define CIRCUITPY_BOARD_UART_PIN {{.rx = &pin_GPIO41, .tx = &pin_GPIO42}} + +// SPI0 is used by the display connector +// SPI1 is used by a two secondary flash chips (one unpopulated) +#define CIRCUITPY_BOARD_SPI (2) +#define CIRCUITPY_BOARD_SPI_PIN \ + {{.clock = &pin_GPIO7, .mosi = &pin_GPIO9}, \ + {.clock = &pin_GPIO13, .mosi = &pin_GPIO11, .miso = &pin_GPIO12} \ + } diff --git a/ports/espressif/boards/seeed_xiao_esp32_s3_trmnl_diy_kit/mpconfigboard.mk b/ports/espressif/boards/seeed_xiao_esp32_s3_trmnl_diy_kit/mpconfigboard.mk new file mode 100644 index 00000000000..9a6b15aa76c --- /dev/null +++ b/ports/espressif/boards/seeed_xiao_esp32_s3_trmnl_diy_kit/mpconfigboard.mk @@ -0,0 +1,32 @@ +USB_VID = 0x2886 +USB_PID = 0x0300 + +USB_PRODUCT = "Seeed Xiao ESP32-S3 Trmnl DIY Kit" +USB_MANUFACTURER = "Seeed Studio" + +IDF_TARGET = esp32s3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_SIZE = 16MB +CIRCUITPY_ESP_FLASH_FREQ = 80m + +CIRCUITPY_ESP_PSRAM_MODE = opi +CIRCUITPY_ESP_PSRAM_SIZE = 8MB +CIRCUITPY_ESP_PSRAM_FREQ = 80m + +CIRCUITPY_FREQUENCYIO = 0 +CIRCUITPY_RAINBOWIO = 0 +CIRCUITPY__EVE = 0 +CIRCUITPY_PS2IO = 0 +CIRCUITPY_CANIO = 0 +CIRCUITPY_MAX3421E = 0 +CIRCUITPY_ESPCAMERA = 0 +CIRCUITPY_PARALLELDISPLAYBUS = 0 +CIRCUITPY_COUNTIO = 0 +CIRCUITPY_NEOPIXEL_WRITE = 0 +CIRCUITPY_ROTARYIO = 0 + +CIRCUITPY_AUDIOCORE = 0 +CIRCUITPY_AUDIOBUSIO = 0 +CIRCUITPY_AUDIOBUSIO_PDMIN = 0 +CIRCUITPY_AUDIOI2SIN = 0 diff --git a/ports/espressif/boards/seeed_xiao_esp32_s3_trmnl_diy_kit/pins.c b/ports/espressif/boards/seeed_xiao_esp32_s3_trmnl_diy_kit/pins.c new file mode 100644 index 00000000000..422f16b787b --- /dev/null +++ b/ports/espressif/boards/seeed_xiao_esp32_s3_trmnl_diy_kit/pins.c @@ -0,0 +1,47 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_BAT_ADC), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_BTN1), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_BTN2), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_EDP_BUSY), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_BTN3), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_ADC_EN), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_SPI0_CLK), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_SPI0_MOSI), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_EDP_DC), MP_ROM_PTR(&pin_GPIO10) }, + + // connected to two additional flash chips (one unpopulated) + { MP_ROM_QSTR(MP_QSTR_SPI1_MOSI), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_SPI1_MISO), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_SPI1_CLK), MP_ROM_PTR(&pin_GPIO13) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_EDP_RES), MP_ROM_PTR(&pin_GPIO38) }, + + // connected to two additional flash chips (CS0 unpopulated) + { MP_ROM_QSTR(MP_QSTR_SPI1_CS0), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_SPI1_CS1), MP_ROM_PTR(&pin_GPIO40) }, + + { MP_ROM_QSTR(MP_QSTR_D14), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO41) }, + + { MP_ROM_QSTR(MP_QSTR_D15), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO42) }, + + { MP_ROM_QSTR(MP_QSTR_PWR_EN), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_SPI0_CS), MP_ROM_PTR(&pin_GPIO44) }, + + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/seeed_xiao_esp32_s3_trmnl_diy_kit/sdkconfig b/ports/espressif/boards/seeed_xiao_esp32_s3_trmnl_diy_kit/sdkconfig new file mode 100644 index 00000000000..ce6696bf50f --- /dev/null +++ b/ports/espressif/boards/seeed_xiao_esp32_s3_trmnl_diy_kit/sdkconfig @@ -0,0 +1,11 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# +# end of Component config + +# end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/common-hal/_bleio/Adapter.c b/ports/espressif/common-hal/_bleio/Adapter.c index 4feb6b2b96b..1031ff86a36 100644 --- a/ports/espressif/common-hal/_bleio/Adapter.c +++ b/ports/espressif/common-hal/_bleio/Adapter.c @@ -44,10 +44,6 @@ #include "esp_nimble_hci.h" #include "nvs_flash.h" -#if CIRCUITPY_SETTINGS_TOML -#include "supervisor/shared/settings.h" -#endif - // Status variables used while busy-waiting for events. static volatile bool _nimble_sync; static volatile int _connection_status; @@ -70,8 +66,6 @@ static void _on_sync(void) { // All examples have this. It'd make sense in a header. void ble_store_config_init(void); -char default_ble_name[] = { 'C', 'I', 'R', 'C', 'U', 'I', 'T', 'P', 'Y', 0, 0, 0, 0, 0, 0, 0}; - void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enabled) { const bool is_enabled = common_hal_bleio_adapter_get_enabled(self); @@ -105,27 +99,6 @@ void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enable ble_svc_gatt_init(); ble_svc_ans_init(); - #if CIRCUITPY_SETTINGS_TOML - char ble_name[1 + MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH]; - settings_err_t result = settings_get_str("CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name)); - if (result == SETTINGS_OK) { - ble_svc_gap_device_name_set(ble_name); - } else - #endif - { - uint8_t mac[6]; - esp_read_mac(mac, ESP_MAC_BT); - mp_int_t len = sizeof(default_ble_name) - 1; - default_ble_name[len - 6] = nibble_to_hex_lower[mac[3] >> 4 & 0xf]; - default_ble_name[len - 5] = nibble_to_hex_lower[mac[3] & 0xf]; - default_ble_name[len - 4] = nibble_to_hex_lower[mac[4] >> 4 & 0xf]; - default_ble_name[len - 3] = nibble_to_hex_lower[mac[4] & 0xf]; - default_ble_name[len - 2] = nibble_to_hex_lower[mac[5] >> 4 & 0xf]; - default_ble_name[len - 1] = nibble_to_hex_lower[mac[5] & 0xf]; - default_ble_name[len] = '\0'; // for now we add null for compatibility with C ASCIIZ strings - ble_svc_gap_device_name_set(default_ble_name); - } - // Clear all of the internal connection objects. for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { bleio_connection_internal_t *connection = &bleio_connections[i]; @@ -150,6 +123,8 @@ void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enable if (!_nimble_sync) { mp_raise_RuntimeError(MP_ERROR_TEXT("Update failed")); } + + bleio_adapter_reset_name(self); } else { int ret = nimble_port_stop(); while (xTaskGetHandle("nimble_host") != NULL && !mp_hal_is_interrupted()) { @@ -175,20 +150,17 @@ bleio_address_obj_t *common_hal_bleio_adapter_get_address(bleio_adapter_obj_t *s return NULL; } - bleio_address_obj_t *address = mp_obj_malloc(bleio_address_obj_t, &bleio_address_type); - common_hal_bleio_address_construct(address, address_bytes, BLEIO_ADDRESS_TYPE_RANDOM_STATIC); - return address; + // The cached address is refreshed on every call. + common_hal_bleio_address_construct(&self->address, address_bytes, BLEIO_ADDRESS_TYPE_RANDOM_STATIC); + self->address.base.type = &bleio_address_type; + return &self->address; } bool common_hal_bleio_adapter_set_address(bleio_adapter_obj_t *self, bleio_address_obj_t *address) { if (address->type != BLEIO_ADDRESS_TYPE_RANDOM_STATIC) { return false; } - mp_buffer_info_t bufinfo; - if (!mp_get_buffer(address->bytes, &bufinfo, MP_BUFFER_READ)) { - return false; - } - int result = ble_hs_id_set_rnd(bufinfo.buf); + int result = ble_hs_id_set_rnd(address->bytes); return result == 0; } @@ -318,9 +290,7 @@ void common_hal_bleio_adapter_stop_scan(bleio_adapter_obj_t *self) { static void _convert_address(const bleio_address_obj_t *address, ble_addr_t *nimble_address) { nimble_address->type = address->type; - mp_buffer_info_t address_buf_info; - mp_get_buffer_raise(address->bytes, &address_buf_info, MP_BUFFER_READ); - memcpy(nimble_address->val, (uint8_t *)address_buf_info.buf, NUM_BLEIO_ADDRESS_BYTES); + memcpy(nimble_address->val, address->bytes, NUM_BLEIO_ADDRESS_BYTES); } static int _mtu_reply(uint16_t conn_handle, @@ -339,7 +309,7 @@ static int _mtu_reply(uint16_t conn_handle, return 0; } -static void _new_connection(uint16_t conn_handle) { +static void _new_connection(uint16_t conn_handle, bool user_owned) { // Set the tx_power for the connection higher than the advertisement. esp_ble_tx_power_set(conn_handle, ESP_PWR_LVL_N0); @@ -362,6 +332,7 @@ static void _new_connection(uint16_t conn_handle) { connection->connection_obj = mp_const_none; connection->pair_status = PAIR_NOT_PAIRED; connection->mtu = 0; + connection->user_owned = user_owned; ble_gattc_exchange_mtu(conn_handle, _mtu_reply, connection); @@ -379,7 +350,8 @@ static int _connect_event(struct ble_gap_event *event, void *self_in) { case BLE_GAP_EVENT_CONNECT: if (event->connect.status == 0) { // This triggers an MTU exchange. Its reply will exit the loop waiting for a connection. - _new_connection(event->connect.conn_handle); + // Only user code connects in the central role. + _new_connection(event->connect.conn_handle, true); // Set connections objs back to NULL since we have a new // connection and need a new tuple. self->connection_objs = NULL; @@ -498,7 +470,8 @@ static int _advertising_event(struct ble_gap_event *event, void *self_in) { #if !MYNEWT_VAL(BLE_EXT_ADV) if (event->connect.status == NIMBLE_OK) { - _new_connection(event->connect.conn_handle); + // The connection belongs to whoever started the advertising it answered. + _new_connection(event->connect.conn_handle, self->user_advertising); // Set connections objs back to NULL since we have a new // connection and need a new tuple. self->connection_objs = NULL; @@ -511,7 +484,8 @@ static int _advertising_event(struct ble_gap_event *event, void *self_in) { case BLE_GAP_EVENT_ADV_COMPLETE: #if MYNEWT_VAL(BLE_EXT_ADV) if (event->adv_complete.reason == NIMBLE_OK) { - _new_connection(event->adv_complete.conn_handle); + // The connection belongs to whoever started the advertising it answered. + _new_connection(event->adv_complete.conn_handle, self->user_advertising); // Set connections objs back to NULL since we have a new // connection and need a new tuple. self->connection_objs = NULL; @@ -837,13 +811,13 @@ void bleio_adapter_reset(bleio_adapter_obj_t *adapter) { // Wait up to 125 ms (128 ticks) for disconnect to complete. This should be // greater than most connection intervals. - bool any_connected = false; + bool any_connected; uint64_t start_ticks = supervisor_ticks_ms64(); - while (any_connected && supervisor_ticks_ms64() - start_ticks < 128) { + do { any_connected = false; for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { bleio_connection_internal_t *connection = &bleio_connections[i]; any_connected |= connection->conn_handle != BLEIO_HANDLE_INVALID; } - } + } while (any_connected && supervisor_ticks_ms64() - start_ticks < 128); } diff --git a/ports/espressif/common-hal/_bleio/Adapter.h b/ports/espressif/common-hal/_bleio/Adapter.h index 1dbae9946ee..605be032406 100644 --- a/ports/espressif/common-hal/_bleio/Adapter.h +++ b/ports/espressif/common-hal/_bleio/Adapter.h @@ -11,6 +11,7 @@ #include "py/obj.h" #include "py/objtuple.h" +#include "shared-bindings/_bleio/Address.h" #include "shared-bindings/_bleio/Connection.h" #include "shared-bindings/_bleio/ScanResults.h" @@ -31,6 +32,10 @@ typedef struct { mp_obj_tuple_t *connection_objs; background_callback_t background_callback; bool user_advertising; + // Cached local address returned by common_hal_bleio_adapter_get_address(). + // Stored inline so it never needs to allocate, even when read before the + // heap is available (e.g. from bleio_adapter_reset_name). + bleio_address_obj_t address; } bleio_adapter_obj_t; void bleio_adapter_gc_collect(bleio_adapter_obj_t *adapter); diff --git a/ports/espressif/common-hal/_bleio/Characteristic.c b/ports/espressif/common-hal/_bleio/Characteristic.c index ff9ab35c1b9..12180b7bfc9 100644 --- a/ports/espressif/common-hal/_bleio/Characteristic.c +++ b/ports/espressif/common-hal/_bleio/Characteristic.c @@ -62,11 +62,18 @@ void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, read_perm == SECURITY_MODE_SIGNED_WITH_MITM || write_perm == SECURITY_MODE_SIGNED_WITH_MITM) { mp_raise_NotImplementedError(MP_ERROR_TEXT("MITM security not supported")); } + // The BLE_GATT_CHR_F_NOTIFY_INDICATE_* flags are set below to require encryption or + // authentication when writing the auto-generated CCCD, if reading the + // characteristic requires it. This matches the nordic port behavior. + // Without the flags, NimBLE registers the CCCD as writable on an unencrypted link, + // so an unpaired central can subscribe and nothing ever requires it to pair. + // + // TODO: This behavior was fixed in NimBLE 1.10.0. ESP-IDF 6.0.1 uses a fork of NimBLE. if (read_perm == SECURITY_MODE_ENC_NO_MITM) { - self->flags |= BLE_GATT_CHR_F_READ_ENC; + self->flags |= BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_NOTIFY_INDICATE_ENC; } if (read_perm == SECURITY_MODE_SIGNED_NO_MITM) { - self->flags |= BLE_GATT_CHR_F_READ_AUTHEN; + self->flags |= BLE_GATT_CHR_F_READ_AUTHEN | BLE_GATT_CHR_F_NOTIFY_INDICATE_AUTHEN; } if (write_perm == SECURITY_MODE_ENC_NO_MITM) { self->flags |= BLE_GATT_CHR_F_WRITE_ENC; diff --git a/ports/espressif/common-hal/_bleio/Connection.c b/ports/espressif/common-hal/_bleio/Connection.c index 91d2336ee68..a063f3ee1a8 100644 --- a/ports/espressif/common-hal/_bleio/Connection.c +++ b/ports/espressif/common-hal/_bleio/Connection.c @@ -34,6 +34,7 @@ #include "freertos/queue.h" #include "host/ble_att.h" +#include "host/ble_store.h" // Uncomment to turn on debug logging just in this file. // #undef CIRCUITPY_VERBOSE_BLE @@ -81,6 +82,22 @@ int bleio_connection_event_cb(struct ble_gap_event *event, void *connection_in) } break; } + case BLE_GAP_EVENT_REPEAT_PAIRING: { + // The peer is asking to pair even though we still have a bond with it. + // This means it no longer has its own copy of that bond. Discard our bond + // and tell the BLE stack to carry on pairing. Otherwise NimBLE silently + // ignores the request, and a central that has forgotten this device can + // never pair again without erasing the bonds on the board. The + // nordic port likewise replaces the stored keys when a peer re-pairs. + struct ble_gap_conn_desc desc; + if (ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc) != 0) { + return BLE_GAP_REPEAT_PAIRING_IGNORE; + } + ble_store_util_delete_peer(&desc.peer_id_addr); + // The BLE stack checks that the bond is really gone before retrying, and + // reports this event again if it is not. + return BLE_GAP_REPEAT_PAIRING_RETRY; + } case BLE_GAP_EVENT_MTU: { if (event->mtu.conn_handle != connection->conn_handle) { return 0; diff --git a/ports/espressif/common-hal/_bleio/Connection.h b/ports/espressif/common-hal/_bleio/Connection.h index 5f33eb43b5d..176ed0ebfe4 100644 --- a/ports/espressif/common-hal/_bleio/Connection.h +++ b/ports/espressif/common-hal/_bleio/Connection.h @@ -31,6 +31,11 @@ typedef enum { typedef struct { uint16_t conn_handle; bool is_central; + // True if user code initiated or accepted this connection: it connected in the + // central role, or a central answered user code's advertising. User-owned + // connections are disconnected when the VM resets; the BLE workflow connection + // is not user-owned and stays up. + bool user_owned; // Remote services discovered when this peripheral is acting as a client. mp_obj_list_t *remote_service_list; // The advertising data and scan response buffers are held by us, not by the SD, so we must diff --git a/ports/espressif/common-hal/_bleio/__init__.c b/ports/espressif/common-hal/_bleio/__init__.c index 552ba0575b3..9fe86405498 100644 --- a/ports/espressif/common-hal/_bleio/__init__.c +++ b/ports/espressif/common-hal/_bleio/__init__.c @@ -24,6 +24,8 @@ #include "common-hal/_bleio/__init__.h" #include "common-hal/_bleio/ble_events.h" +#include "services/gatt/ble_svc_gatt.h" + #include "nvs_flash.h" static volatile int _completion_status; @@ -35,10 +37,29 @@ void bleio_user_reset(void) { if (!common_hal_bleio_adapter_get_enabled(&common_hal_bleio_adapter_obj)) { return; } - // Stop any user scanning or advertising, and stop all connections. - // TODO: Don't stop BLE workflow connection. - bleio_adapter_reset(&common_hal_bleio_adapter_obj); + // Stop any user scanning or advertising. + common_hal_bleio_adapter_stop_scan(&common_hal_bleio_adapter_obj); + common_hal_bleio_adapter_stop_advertising(&common_hal_bleio_adapter_obj); + + // Disconnect the connections that user code initiated or accepted with its own + // advertising. Keep the BLE workflow connection if present. + // + // Remove each connection's pointers into the VM heap before disconnecting: + // the heap is about to go away, and a disconnect completes asynchronously. + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connection_internal_t *connection = &bleio_connections[i]; + connection->connection_obj = mp_const_none; + connection->remote_service_list = NULL; + if (connection->conn_handle != BLEIO_HANDLE_INVALID && connection->user_owned) { + common_hal_bleio_connection_disconnect(connection); + } + } + + // Now clear the adapter's remaining heap pointer, since it will be stale + // when the VM stops. + common_hal_bleio_adapter_obj.connection_objs = NULL; + // Also clean up event handlers that are on the heap. ble_event_remove_heap_handlers(); // Maybe start advertising the BLE workflow. @@ -52,11 +73,31 @@ void bleio_reset(void) { return; } + // The stop/start cycle below clears user-created services from the GATT + // table, and drops every connection, BLE workflow included. So run it + // only when user code created services. All other user BLE state has already + // been torn down individually by bleio_user_reset(). + // + // TODO: ESP-IDF NimBLE can delete individual services (ble_gatts_delete_svc(), + // used in Service.c), so bleio_user_reset() could delete user services one by + // one and never restart the BLE stack at all. For now this port matches + // nordic, whose SoftDevice can only clear services with a full cycle. + if (!bleio_get_user_services_created()) { + return; + } + bleio_clear_user_services_created(); + supervisor_stop_bluetooth(); ble_event_reset(); bleio_adapter_reset(&common_hal_bleio_adapter_obj); common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, false); supervisor_start_bluetooth(); + + // The stop/start above rebuilt the GATT table, so now any bonded peers' cached + // tables are stale. Signal Service Changed over the whole handle range. + // Without Service Changed, a bonded host trusts its cache indefinitely and can + // look up characteristics at stale handles. + ble_svc_gatt_changed(0x0001, 0xffff); } // The singleton _bleio.Adapter object, bound to _bleio.adapter diff --git a/ports/espressif/common-hal/_bleio/ble_events.c b/ports/espressif/common-hal/_bleio/ble_events.c index b57362fe007..d2474d1e00f 100644 --- a/ports/espressif/common-hal/_bleio/ble_events.c +++ b/ports/espressif/common-hal/_bleio/ble_events.c @@ -28,11 +28,14 @@ void ble_event_reset(void) { void ble_event_remove_heap_handlers(void) { ble_event_handler_entry_t *it = MP_STATE_VM(ble_event_handler_entries); while (it != NULL) { - // If the param is on the heap, then delete the handler. - if (gc_ptr_on_heap(it->param)) { + // Save it->next before removing, because removing clears it. + ble_event_handler_entry_t *next = it->next; + // Remove the handler if the entry or its param is on the heap, which is + // about to go away. + if (gc_ptr_on_heap(it) || gc_ptr_on_heap(it->param)) { ble_event_remove_handler(it->func, it->param); } - it = it->next; + it = next; } } diff --git a/ports/espressif/common-hal/audioi2sin/I2SIn.c b/ports/espressif/common-hal/audioi2sin/I2SIn.c index 6da790d9736..9a85d11f493 100644 --- a/ports/espressif/common-hal/audioi2sin/I2SIn.c +++ b/ports/espressif/common-hal/audioi2sin/I2SIn.c @@ -25,7 +25,7 @@ void common_hal_audioi2sin_i2sin_construct(audioi2sin_i2sin_obj_t *self, const mcu_pin_obj_t *data, const mcu_pin_obj_t *main_clock, uint32_t sample_rate, uint8_t bit_depth, uint8_t output_bit_depth, bool mono, bool left_justified, bool samples_signed, - bool external_clock, bool invert_bit_clock) { + bool external_clock) { if (external_clock) { mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_external_clock); } diff --git a/ports/espressif/common-hal/mdns/RemoteService.c b/ports/espressif/common-hal/mdns/RemoteService.c index 515a3f7bf02..3d80199397e 100644 --- a/ports/espressif/common-hal/mdns/RemoteService.c +++ b/ports/espressif/common-hal/mdns/RemoteService.c @@ -9,56 +9,27 @@ #include "shared-bindings/ipaddress/IPv4Address.h" const char *common_hal_mdns_remoteservice_get_service_type(mdns_remoteservice_obj_t *self) { - if (self->result == NULL) { - return ""; - } - return self->result->service_type; + return self->service_name; } const char *common_hal_mdns_remoteservice_get_protocol(mdns_remoteservice_obj_t *self) { - if (self->result == NULL) { - return ""; - } - return self->result->proto; + return self->protocol; } const char *common_hal_mdns_remoteservice_get_instance_name(mdns_remoteservice_obj_t *self) { - if (self->result == NULL) { - return ""; - } - return self->result->instance_name; + return self->instance_name; } const char *common_hal_mdns_remoteservice_get_hostname(mdns_remoteservice_obj_t *self) { - if (self->result == NULL) { - return ""; - } - return self->result->hostname; + return self->hostname; } mp_int_t common_hal_mdns_remoteservice_get_port(mdns_remoteservice_obj_t *self) { - if (self->result == NULL) { - return 0; - } - return self->result->port; + return self->port; } uint32_t mdns_remoteservice_get_ipv4_address(mdns_remoteservice_obj_t *self) { - if (self->result == NULL || - self->result->ip_protocol != MDNS_IP_PROTOCOL_V4 || - self->result->addr == NULL) { - return 0; - } - mdns_ip_addr_t *cur = self->result->addr; - while (cur != NULL) { - if (cur->addr.type == ESP_IPADDR_TYPE_V4) { - return cur->addr.u_addr.ip4.addr; - } - - cur = cur->next; - } - - return 0; + return self->ipv4_address; } mp_obj_t common_hal_mdns_remoteservice_get_ipv4_address(mdns_remoteservice_obj_t *self) { @@ -68,8 +39,3 @@ mp_obj_t common_hal_mdns_remoteservice_get_ipv4_address(mdns_remoteservice_obj_t } return common_hal_ipaddress_new_ipv4address(addr); } - -void common_hal_mdns_remoteservice_deinit(mdns_remoteservice_obj_t *self) { - mdns_query_results_free(self->result); - self->result = NULL; -} diff --git a/ports/espressif/common-hal/mdns/RemoteService.h b/ports/espressif/common-hal/mdns/RemoteService.h index 6fb3000c7d1..89ff69be29d 100644 --- a/ports/espressif/common-hal/mdns/RemoteService.h +++ b/ports/espressif/common-hal/mdns/RemoteService.h @@ -8,7 +8,15 @@ #include "mdns.h" +// The IDF's mdns_result_t lives on the IDF heap and is only valid while mdns +// is inited. Copy what we need into the object instead so that the object's +// lifetime is independent of the mdns.Server's. typedef struct { mp_obj_base_t base; - mdns_result_t *result; + uint32_t ipv4_address; + uint16_t port; + char protocol[5]; // RFC 6763 Section 7.2 - 4 bytes + 1 for NUL + char service_name[17]; // RFC 6763 Section 7.2 - 16 bytes + 1 for NUL + char instance_name[64]; // RFC 6763 Section 7.2 - 63 bytes + 1 for NUL + char hostname[64]; // RFC 6762 Appendix A - 63 bytes for label + 1 for NUL } mdns_remoteservice_obj_t; diff --git a/ports/espressif/common-hal/mdns/Server.c b/ports/espressif/common-hal/mdns/Server.c index e8c34ee0885..7eaa9a442a2 100644 --- a/ports/espressif/common-hal/mdns/Server.c +++ b/ports/espressif/common-hal/mdns/Server.c @@ -6,6 +6,8 @@ #include "shared-bindings/mdns/Server.h" +#include + #include "py/gc.h" #include "py/runtime.h" #include "shared-bindings/mdns/RemoteService.h" @@ -18,6 +20,13 @@ // could be created.) static mdns_server_obj_t *_active_object = NULL; +// strlcpy(), but a NULL src yields an empty string instead of undefined +// behavior. The IDF leaves mdns_result_t fields NULL when a query didn't +// resolve them. +static void strlcpy_or_empty(char *dest, const char *src, size_t dest_len) { + strlcpy(dest, src == NULL ? "" : src, dest_len); +} + void mdns_server_construct(mdns_server_obj_t *self, bool workflow) { if (_active_object != NULL) { if (self == _active_object) { @@ -33,9 +42,12 @@ void mdns_server_construct(mdns_server_obj_t *self, bool workflow) { } _active_object = self; + self->instance_name[0] = '\0'; + // Match the netif hostname set when `import wifi` was called. - esp_netif_get_hostname(common_hal_wifi_radio_obj.netif, &self->hostname); - common_hal_mdns_server_set_hostname(self, self->hostname); + const char *netif_hostname; + esp_netif_get_hostname(common_hal_wifi_radio_obj.netif, &netif_hostname); + common_hal_mdns_server_set_hostname(self, netif_hostname); self->inited = true; @@ -95,11 +107,11 @@ void common_hal_mdns_server_set_hostname(mdns_server_obj_t *self, const char *ho while (!mdns_hostname_exists(hostname)) { RUN_BACKGROUND_TASKS; } - self->hostname = hostname; + strlcpy_or_empty(self->hostname, hostname, sizeof(self->hostname)); } const char *common_hal_mdns_server_get_instance_name(mdns_server_obj_t *self) { - if (self->instance_name == NULL) { + if (self->instance_name[0] == '\0') { return self->hostname; } return self->instance_name; @@ -107,7 +119,28 @@ const char *common_hal_mdns_server_get_instance_name(mdns_server_obj_t *self) { void common_hal_mdns_server_set_instance_name(mdns_server_obj_t *self, const char *instance_name) { mdns_instance_name_set(instance_name); - self->instance_name = instance_name; + strlcpy_or_empty(self->instance_name, instance_name, sizeof(self->instance_name)); +} + +// Copy everything we expose out of the IDF's result so that the RemoteService +// no longer references IDF-owned memory. The caller is responsible for freeing +// the result itself. +static void copy_data_into_remote_service(mdns_result_t *result, mdns_remoteservice_obj_t *out) { + out->base.type = &mdns_remoteservice_type; + out->port = result->port; + out->ipv4_address = 0; + if (result->ip_protocol == MDNS_IP_PROTOCOL_V4) { + for (mdns_ip_addr_t *cur = result->addr; cur != NULL; cur = cur->next) { + if (cur->addr.type == ESP_IPADDR_TYPE_V4) { + out->ipv4_address = cur->addr.u_addr.ip4.addr; + break; + } + } + } + strlcpy_or_empty(out->protocol, result->proto, sizeof(out->protocol)); + strlcpy_or_empty(out->service_name, result->service_type, sizeof(out->service_name)); + strlcpy_or_empty(out->instance_name, result->instance_name, sizeof(out->instance_name)); + strlcpy_or_empty(out->hostname, result->hostname, sizeof(out->hostname)); } size_t mdns_server_find(mdns_server_obj_t *self, const char *service_type, const char *protocol, @@ -123,23 +156,15 @@ size_t mdns_server_find(mdns_server_obj_t *self, const char *service_type, const } mdns_query_async_delete(search); mdns_result_t *next = results; - // Don't error if we're out of memory. Instead, truncate the tuple. - uint8_t added = 0; + // Truncate if we don't have space for everything the IDF found. + size_t added = 0; while (next != NULL && added < out_len) { - mdns_remoteservice_obj_t *service = &out[added]; - - service->result = next; - service->base.type = &mdns_remoteservice_type; + copy_data_into_remote_service(next, &out[added]); next = next->next; - // Break the linked list so we free each result separately. - service->result->next = NULL; added++; } - if (added < out_len) { - // Free the remaining results from the IDF because we don't have - // enough space in Python. - mdns_query_results_free(next); - } + // We've copied out everything we need, so release the IDF's copy. + mdns_query_results_free(results); return num_results; } @@ -158,36 +183,40 @@ mp_obj_t common_hal_mdns_server_find(mdns_server_obj_t *self, const char *servic // The empty tuple object is shared and stored in flash so return early if // we got it. Without this we'll crash when trying to set len below. if (num_results == 0) { + mdns_query_results_free(results); return MP_OBJ_FROM_PTR(tuple); } mdns_result_t *next = results; // Don't error if we're out of memory. Instead, truncate the tuple. uint8_t added = 0; while (next != NULL) { - mdns_remoteservice_obj_t *service = gc_alloc(sizeof(mdns_remoteservice_obj_t), GC_ALLOC_FLAG_HAS_FINALISER); + mdns_remoteservice_obj_t *service = m_malloc_maybe(sizeof(mdns_remoteservice_obj_t)); if (service == NULL) { if (added == 0) { + mdns_query_results_free(results); m_malloc_fail(sizeof(mdns_remoteservice_obj_t)); } - // Free the remaining results from the IDF because we don't have - // enough space in Python. - mdns_query_results_free(next); break; } - service->result = next; - service->base.type = &mdns_remoteservice_type; + copy_data_into_remote_service(next, service); next = next->next; - // Break the linked list so we free each result separately. - service->result->next = NULL; tuple->items[added] = MP_OBJ_FROM_PTR(service); added++; } tuple->len = added; + // We've copied out everything we need, so release the IDF's copy. + mdns_query_results_free(results); + return MP_OBJ_FROM_PTR(tuple); } void common_hal_mdns_server_advertise_service(mdns_server_obj_t *self, const char *service_type, const char *protocol, mp_int_t port, const char *txt_records[], size_t num_txt_records) { + // Reject rather than silently drop them. See the TODO below. + if (num_txt_records > 0) { + mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_txt_records); + } + if (mdns_service_exists(service_type, protocol, NULL)) { mdns_service_port_set(service_type, protocol, port); } else { diff --git a/ports/espressif/common-hal/mdns/Server.h b/ports/espressif/common-hal/mdns/Server.h index f364a539c91..e7bfabb9528 100644 --- a/ports/espressif/common-hal/mdns/Server.h +++ b/ports/espressif/common-hal/mdns/Server.h @@ -10,8 +10,11 @@ typedef struct { mp_obj_base_t base; - const char *hostname; - const char *instance_name; + // Store copies rather than the caller's pointers. The setters are handed + // GC-heap strings, which are recycled when the VM resets while this object + // (and the web workflow's static one) lives on. + char hostname[64]; // RFC 6762 Appendix A - 63 bytes for label + 1 for NUL + char instance_name[64]; // RFC 6763 Section 7.2 - 63 bytes + 1 for NUL // Track if this object owns access to the underlying MDNS service. bool inited; } mdns_server_obj_t; diff --git a/ports/espressif/common-hal/picogame/Display.c b/ports/espressif/common-hal/picogame/Display.c new file mode 100644 index 00000000000..635549e5918 --- /dev/null +++ b/ports/espressif/common-hal/picogame/Display.c @@ -0,0 +1,153 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#include "common-hal/picogame/Display.h" + +#include + +#include "py/runtime.h" +#include "shared-module/picogame/__init__.h" +#include "shared-module/displayio/display_core.h" +#include "shared-bindings/displayio/__init__.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "common-hal/busio/SPI.h" + +#include "driver/spi_master.h" + +// The esp-idf SPI device queue holds MAX_SPI_TRANSACTIONS (10) outstanding +// transactions. We keep up to two strips in flight (current transferring while +// the next is blitted), so each strip may use at most this many DMA chunks and +// still leave room: 2 * 5 <= 10. Strips needing more chunks fall back to a +// blocking send (correct, just no overlap for that strip). +#define PICOGAME_MAX_STRIP_CHUNKS 5 + +void common_hal_picogame_display_construct(picogame_display_obj_t *self, + busdisplay_busdisplay_obj_t *display, bool rgb444) { + self->display = display; + // RGB444 strip packing isn't implemented on this backend yet. Raise rather than silently + // ignore it: a no-op would leave the panel in RGB565 while the caller expects 444 (garbled + // output / wrong byte count). The rpi backend implements it; until this one does, fail loud. + if (rgb444) { + mp_arg_error_invalid(MP_QSTR_rgb444); // not implemented on this port yet + } + self->rgb444 = false; + + // The fast path queues raw DMA on the display's SPI device; only FourWire + // SPI buses expose one. + if (!mp_obj_is_type(display->bus.bus, &fourwire_fourwire_type)) { + mp_arg_error_invalid(MP_QSTR_display); // the fast backend drives a FourWire SPI panel only + } + fourwire_fourwire_obj_t *fw = MP_OBJ_TO_PTR(display->bus.bus); + self->spi = common_hal_busio_spi_get_device_handle(fw->bus); +} + +// Retrieve all outstanding results for *count queued chunks, then zero the count. +static void drain(spi_device_handle_t spi, int *count) { + spi_transaction_t *rtrans; + while (*count > 0) { + spi_device_get_trans_result(spi, &rtrans, portMAX_DELAY); + (*count)--; + } +} + +// Queue one strip (nbytes from buf) as up to PICOGAME_MAX_STRIP_CHUNKS DMA +// transactions. Returns the chunk count, or -1 if it would need more chunks. +static int queue_strip(spi_device_handle_t spi, spi_transaction_t *trans, + const uint16_t *buf, size_t nbytes) { + int needed = (int)((nbytes + SPI_MAX_DMA_LEN - 1) / SPI_MAX_DMA_LEN); + if (needed > PICOGAME_MAX_STRIP_CHUNKS) { + return -1; + } + const uint8_t *p = (const uint8_t *)buf; + size_t off = 0; + int n = 0; + while (off < nbytes) { + size_t chunk = nbytes - off; + if (chunk > SPI_MAX_DMA_LEN) { + chunk = SPI_MAX_DMA_LEN; + } + memset(&trans[n], 0, sizeof(spi_transaction_t)); + trans[n].length = chunk * 8; // in bits + trans[n].tx_buffer = p + off; + spi_device_queue_trans(spi, &trans[n], portMAX_DELAY); + off += chunk; + n++; + } + return n; +} + +void common_hal_picogame_display_render(picogame_display_obj_t *self, + mp_obj_t *items, uint8_t *kinds, size_t n, + uint16_t *buf_a, uint16_t *buf_b, size_t buf_pixels, + int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t background, + int ox, int oy) { + + busdisplay_busdisplay_obj_t *display = self->display; + spi_device_handle_t spi = self->spi; + + // Open the GRAM window (set region, begin transaction, RAMWR). After the + // first DATA send raises DC, DC stays high for the raw queued strips that + // follow, and CS stays low until end_transaction. + int region_w, strip_h; + int cx0 = x0, cy0 = y0, cx1 = x1, cy1 = y1; // strip_begin clamps these to the panel in place + if (!picogame_strip_begin(display, &cx0, &cy0, &cx1, &cy1, buf_pixels, ®ion_w, &strip_h)) { + return; + } + + uint16_t *bufs[2] = { buf_a, buf_b }; + spi_transaction_t trans[2][PICOGAME_MAX_STRIP_CHUNKS]; + int inflight[2] = { 0, 0 }; // chunks queued from bufs[i], awaiting result + + // A StripDraw callback may latch a BaseException (Ctrl-C / auto-reload). Like the portable + // renderer and the RP backend, re-raise it -- but only AFTER the queued transfers drain and + // the bus transaction closes, so hold it here and propagate below. + mp_obj_t pending = MP_OBJ_NULL; + + int cur = 0; + bool first = true; + for (int sy = cy0; sy < cy1; sy += strip_h) { + int sh = picogame_imin(strip_h, cy1 - sy); + size_t nbytes = (size_t)region_w * sh * 2; + uint16_t *buf = bufs[cur]; + + // This buffer must be free before we overwrite it. + drain(spi, &inflight[cur]); + + // Blit this strip. If the OTHER buffer has a strip in flight, its DMA + // transfer overlaps this CPU work -- the whole point of the fast path. + pending = picogame_blit_strip_layers(buf, region_w, sy, sh, cx0, items, kinds, n, + background, ox, oy); + + int nch; + if (first) { + // First DATA send goes through the busdisplay so it raises DC. + display->bus.send(display->bus.bus, DISPLAY_DATA, + CHIP_SELECT_UNTOUCHED, (uint8_t *)buf, nbytes); + first = false; + } else if ((nch = queue_strip(spi, trans[cur], buf, nbytes)) >= 0) { + inflight[cur] = nch; + } else { + // Strip too large to keep two in flight: drain everything and send + // it blocking (DC already high, CS untouched). + drain(spi, &inflight[cur ^ 1]); + display->bus.send(display->bus.bus, DISPLAY_DATA, + CHIP_SELECT_UNTOUCHED, (uint8_t *)buf, nbytes); + } + cur ^= 1; + if (pending != MP_OBJ_NULL) { // callback interrupted: this strip is queued, now stop + flush + break; + } + } + + drain(spi, &inflight[0]); + drain(spi, &inflight[1]); + + displayio_display_bus_end_transaction(&display->bus); + + if (pending != MP_OBJ_NULL) { // bus now closed -> safe to re-raise (Ctrl-C / reload) + nlr_raise(MP_OBJ_TO_PTR(pending)); + } +} diff --git a/ports/espressif/common-hal/picogame/Display.h b/ports/espressif/common-hal/picogame/Display.h new file mode 100644 index 00000000000..b4487e5ee55 --- /dev/null +++ b/ports/espressif/common-hal/picogame/Display.h @@ -0,0 +1,33 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT +// +// Fast display backend (espressif): wraps an existing busdisplay and streams +// pixels with the esp-idf SPI master's queued DMA, double-buffered so the CPU +// blits the next strip while the current one transfers. Reuses the busdisplay's +// SPI device, window opcodes and dimensions -- controller/resolution agnostic. + +#pragma once + +#include "py/obj.h" +#include "driver/spi_master.h" +#include "shared-bindings/busdisplay/BusDisplay.h" +#include "shared-module/picogame/Sprite.h" + +typedef struct { + mp_obj_base_t base; + busdisplay_busdisplay_obj_t *display; + spi_device_handle_t spi; // the busdisplay's SPI device (raw DMA queueing) + bool rgb444; // RGB444 strip packing (not yet implemented on this backend) +} picogame_display_obj_t; + +void common_hal_picogame_display_construct(picogame_display_obj_t *self, + busdisplay_busdisplay_obj_t *display, bool rgb444); + +void common_hal_picogame_display_render(picogame_display_obj_t *self, + mp_obj_t *items, uint8_t *kinds, size_t n, + uint16_t *buf_a, uint16_t *buf_b, size_t buf_pixels, + int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t background, + int ox, int oy); diff --git a/ports/espressif/esp-idf-config/sdkconfig-psram-16MB.defaults b/ports/espressif/esp-idf-config/sdkconfig-psram-16MB.defaults new file mode 100644 index 00000000000..f3f2fc60bc6 --- /dev/null +++ b/ports/espressif/esp-idf-config/sdkconfig-psram-16MB.defaults @@ -0,0 +1,5 @@ +# CONFIG_SPIRAM_TYPE_AUTO is not set +# CONFIG_SPIRAM_TYPE_ESPPSRAM16 is not set +# CONFIG_SPIRAM_TYPE_ESPPSRAM32 is not set +# CONFIG_SPIRAM_TYPE_ESPPSRAM64 is not set +CONFIG_SPIRAM_TYPE_AUTO=y diff --git a/ports/espressif/esp-idf-config/sdkconfig.defaults b/ports/espressif/esp-idf-config/sdkconfig.defaults index 941f879ed42..9859e5e94a8 100644 --- a/ports/espressif/esp-idf-config/sdkconfig.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig.defaults @@ -120,7 +120,7 @@ CONFIG_MBEDTLS_SSL_PROTO_DTLS=y # CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED is not set # CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED is not set # CONFIG_MBEDTLS_ECP_FIXED_POINT_OPTIM is not set -# CONFIG_MBEDTLS_ERROR_STRINGS is not set +CONFIG_MBEDTLS_ERROR_STRINGS=y # end of mbedTLS # diff --git a/ports/espressif/mpconfigport.mk b/ports/espressif/mpconfigport.mk index 488591dd120..98fe3224a49 100644 --- a/ports/espressif/mpconfigport.mk +++ b/ports/espressif/mpconfigport.mk @@ -417,5 +417,18 @@ endif USB_NUM_ENDPOINT_PAIRS = 7 USB_NUM_IN_ENDPOINTS = 5 +# usb_audio is compiled in on the S3 but claims no endpoints until +# usb_audio.enable() is called from boot.py, so enabling it by default does not +# affect existing boards. Freeing one IN endpoint (usb_hid.disable()) is then the +# only thing a user needs, with no custom build. +ifeq ($(IDF_TARGET),esp32s3) +# It needs the TinyUSB device stack and audiocore, so leave it off for boards +# that turn either off. The S3 has no AUDIOIO and no AUDIOPWMIO, so audiocore +# follows CIRCUITPY_AUDIOBUSIO here. +ifeq ($(filter 0,$(CIRCUITPY_USB_DEVICE) $(CIRCUITPY_AUDIOCORE) $(CIRCUITPY_AUDIOBUSIO)),) +CIRCUITPY_USB_AUDIO ?= 1 +endif +endif + # Usually lots of flash space available CIRCUITPY_MESSAGE_COMPRESSION_LEVEL ?= 1 diff --git a/ports/espressif/supervisor/port.c b/ports/espressif/supervisor/port.c index 0d47f7ecd19..34cfba32b7e 100644 --- a/ports/espressif/supervisor/port.c +++ b/ports/espressif/supervisor/port.c @@ -16,6 +16,7 @@ #include "py/mpprint.h" #include "py/runtime.h" +#include "esp_mac.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -521,7 +522,12 @@ void port_idle_until_interrupt(void) { #if CIRCUITPY_WIFI void port_boot_info(void) { uint8_t mac[6]; - esp_wifi_get_mac(WIFI_IF_STA, mac); + // This runs before esp_wifi_init(), so esp_wifi_get_mac() fails with + // ESP_ERR_WIFI_NOT_INIT and leaves mac[] untouched. esp_read_mac() reads + // efuse directly and does not need the WiFi driver started. + if (esp_read_mac(mac, ESP_MAC_WIFI_STA) != ESP_OK) { + return; + } mp_printf(&mp_plat_print, "MAC"); for (int i = 0; i < 6; i++) { mp_printf(&mp_plat_print, ":%02X", mac[i]); diff --git a/ports/nordic/Makefile b/ports/nordic/Makefile index 1e283fc699c..ec755fac892 100755 --- a/ports/nordic/Makefile +++ b/ports/nordic/Makefile @@ -6,9 +6,12 @@ include ../../py/circuitpy_mkenv.mk -ifneq ($(SD), ) +# Skip when the make command is not board-specific i.e. fetch-port-submodules. +ifneq ($(VALID_BOARD),) +ifeq ($(CIRCUITPY_BLEIO_NATIVE),1) include bluetooth/bluetooth_common.mk endif +endif CROSS_COMPILE = arm-none-eabi- @@ -115,11 +118,16 @@ SRC_C += \ boards/$(BOARD)/board.c \ boards/$(BOARD)/pins.c \ device/$(MCU_VARIANT)/startup_$(MCU_SUB_VARIANT).c \ + nrfx/mdk/system_$(MCU_SUB_VARIANT).c \ + +ifeq ($(CIRCUITPY_BLEIO_NATIVE),1) +SRC_C += \ bluetooth/ble_drv.c \ common-hal/_bleio/bonding.c \ - nrfx/mdk/system_$(MCU_SUB_VARIANT).c \ sd_mutex.c \ +endif + SRC_PERIPHERALS := \ peripherals/nrf/cache.c \ peripherals/nrf/clocks.c \ @@ -177,7 +185,13 @@ UF2_FAMILY_ID_nrf52840 = 0xADA52840 UF2_FAMILY_ID_nrf52833 = 0x621E937A +ifeq ($(CIRCUITPY_BLEIO_NATIVE),1) all: $(BUILD)/firmware.bin $(BUILD)/firmware.uf2 $(BUILD)/firmware.combined.hex +else +# firmware.combined.hex merges in the SoftDevice hex; without one there is +# nothing to combine. +all: $(BUILD)/firmware.bin $(BUILD)/firmware.uf2 $(BUILD)/firmware.hex +endif ifeq ($(VALID_BOARD),) $(BUILD)/firmware.elf: invalid-board diff --git a/ports/nordic/bluetooth/ble_drv.c b/ports/nordic/bluetooth/ble_drv.c index 7085aa477b5..c2122c8845e 100644 --- a/ports/nordic/bluetooth/ble_drv.c +++ b/ports/nordic/bluetooth/ble_drv.c @@ -141,11 +141,14 @@ void ble_drv_reset(void) { void ble_drv_remove_heap_handlers(void) { ble_drv_evt_handler_entry_t *it = MP_STATE_VM(ble_drv_evt_handler_entries); while (it != NULL) { - // If the param is on the heap, then delete the handler. - if (gc_ptr_on_heap(it->param)) { + // Save it->next before removing, because removing clears it. + ble_drv_evt_handler_entry_t *next = it->next; + // Remove the handler if the entry or its param is on the heap, which is + // about to go away. + if (gc_ptr_on_heap(it) || gc_ptr_on_heap(it->param)) { ble_drv_remove_event_handler(it->func, it->param); } - it = it->next; + it = next; } } diff --git a/ports/nordic/boards/pca10100/mpconfigboard.mk b/ports/nordic/boards/pca10100/mpconfigboard.mk index a081a0db479..9997abb8bfe 100644 --- a/ports/nordic/boards/pca10100/mpconfigboard.mk +++ b/ports/nordic/boards/pca10100/mpconfigboard.mk @@ -9,3 +9,5 @@ INTERNAL_FLASH_FILESYSTEM = 1 CIRCUITPY_ONEWIREIO = 0 CIRCUITPY_AUDIOMIXER = 0 +CIRCUITPY_RAINBOWIO = 0 +CIRCUITPY_USB_MIDI = 0 diff --git a/ports/nordic/common-hal/_bleio/Adapter.c b/ports/nordic/common-hal/_bleio/Adapter.c index 2ca9df89710..ea3155e4da7 100644 --- a/ports/nordic/common-hal/_bleio/Adapter.c +++ b/ports/nordic/common-hal/_bleio/Adapter.c @@ -32,10 +32,6 @@ #include "supervisor/usb.h" #endif -#if CIRCUITPY_SETTINGS_TOML -#include "supervisor/shared/settings.h" -#endif - #define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS) #define BLE_MAX_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS) #define BLE_SLAVE_LATENCY 0 @@ -248,6 +244,19 @@ static bool adapter_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { connection->connection_obj = mp_const_none; connection->pair_status = PAIR_NOT_PAIRED; connection->mtu = 0; + // Only user code connects in the central role, and a peripheral + // connection belongs to whoever started the advertising it answered. + connection->user_owned = connected->role == BLE_GAP_ROLE_CENTRAL || + self->advertising_started_by_user; + // Clear leftover bond state; connection slots are recycled. The + // SoftDevice fills in only the keys the new peer distributes, so a + // stale keyset could mix the previous peer's keys into this peer's + // stored bond, and a stale ediv or pending-save flag could file + // this connection's bond data under the previous peer's key. + connection->ediv = EDIV_INVALID; + connection->do_bond_cccds = false; + connection->do_bond_keys = false; + bonding_clear_keys(&connection->bonding_keys); ble_drv_add_event_handler_entry(&connection->handler_entry, connection_on_ble_evt, connection); self->connection_objs = NULL; @@ -310,31 +319,6 @@ static void get_address(bleio_adapter_obj_t *self, ble_gap_addr_t *address) { check_nrf_error(sd_ble_gap_addr_get(address)); } -char default_ble_name[] = { 'C', 'I', 'R', 'C', 'U', 'I', 'T', 'P', 'Y', 0, 0, 0, 0, 0}; - -static void bleio_adapter_reset_name(bleio_adapter_obj_t *self) { - // setup the default name - ble_gap_addr_t addr; // local_address - get_address(self, &addr); - mp_int_t len = sizeof(default_ble_name) - 1; - default_ble_name[len - 4] = nibble_to_hex_lower[addr.addr[1] >> 4 & 0xf]; - default_ble_name[len - 3] = nibble_to_hex_lower[addr.addr[1] & 0xf]; - default_ble_name[len - 2] = nibble_to_hex_lower[addr.addr[0] >> 4 & 0xf]; - default_ble_name[len - 1] = nibble_to_hex_lower[addr.addr[0] & 0xf]; - default_ble_name[len] = '\0'; // for now we add null for compatibility with C ASCIIZ strings - - #if CIRCUITPY_SETTINGS_TOML - char ble_name[32]; - - settings_err_t result = settings_get_str("CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name)); - if (result == SETTINGS_OK) { - common_hal_bleio_adapter_set_name(self, ble_name); - return; - } - #endif - - common_hal_bleio_adapter_set_name(self, (char *)default_ble_name); -} static void bluetooth_adapter_background(void *data) { supervisor_bluetooth_background(); @@ -376,6 +360,9 @@ void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enable ble_drv_remove_event_handler(connection_on_ble_evt, connection); connection->conn_handle = BLE_CONN_HANDLE_INVALID; } + // The SoftDevice's GATT table is empty after an enable. + bleio_gatts_min_handle = 0xFFFF; + bleio_gatts_max_handle = 0; self->background_callback.fun = bluetooth_adapter_background; self->background_callback.data = self; bleio_adapter_reset_name(self); @@ -408,20 +395,19 @@ bleio_address_obj_t *common_hal_bleio_adapter_get_address(bleio_adapter_obj_t *s ble_gap_addr_t local_address; get_address(self, &local_address); - bleio_address_obj_t *address = mp_obj_malloc(bleio_address_obj_t, &bleio_address_type); - - common_hal_bleio_address_construct(address, local_address.addr, local_address.addr_type); - return address; + // Return the address cached on the adapter so this can be called before + // the heap is available (e.g. from bleio_adapter_reset_name). The shared + // common_hal_bleio_address_construct() converts the raw address bytes + // into the Address object. The cached address is refreshed on every call. + common_hal_bleio_address_construct(&self->address, local_address.addr, local_address.addr_type); + self->address.base.type = &bleio_address_type; + return &self->address; } bool common_hal_bleio_adapter_set_address(bleio_adapter_obj_t *self, bleio_address_obj_t *address) { ble_gap_addr_t local_address; - mp_buffer_info_t bufinfo; - if (!mp_get_buffer(address->bytes, &bufinfo, MP_BUFFER_READ)) { - return false; - } local_address.addr_type = address->type; - memcpy(local_address.addr, bufinfo.buf, NUM_BLEIO_ADDRESS_BYTES); + memcpy(local_address.addr, address->bytes, NUM_BLEIO_ADDRESS_BYTES); return sd_ble_gap_addr_set(&local_address) == NRF_SUCCESS; } @@ -610,9 +596,7 @@ static bool connect_on_ble_evt(ble_evt_t *ble_evt, void *info_in) { static void _convert_address(const bleio_address_obj_t *address, ble_gap_addr_t *sd_address) { sd_address->addr_type = address->type; - mp_buffer_info_t address_buf_info; - mp_get_buffer_raise(address->bytes, &address_buf_info, MP_BUFFER_READ); - memcpy(sd_address->addr, (uint8_t *)address_buf_info.buf, NUM_BLEIO_ADDRESS_BYTES); + memcpy(sd_address->addr, address->bytes, NUM_BLEIO_ADDRESS_BYTES); } mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout) { @@ -739,6 +723,9 @@ uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, if (self->current_advertising_data != NULL && self->current_advertising_data == self->advertising_data) { return NRF_ERROR_BUSY; } + // The supervisor calls this internal function directly; user code arrives via + // common_hal_bleio_adapter_start_advertising(), which overrides this to true. + self->advertising_started_by_user = false; // If the current advertising data isn't owned by the adapter then it must be an internal // advertisement that we should stop. @@ -794,7 +781,13 @@ uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, // advertising. This prevents a potential race condition where we // fire off a beacon with the same advertising data but a new MAC // address just as we tear down the connection. - .private_addr_cycle_s = timeout + 1, + // + // For unlimited advertising, timeout + 1 would rotate the address + // every second, too fast for a central to resolve it and connect. + // Zero selects the SoftDevice default cycle of 15 minutes + // (BLE_GAP_DEFAULT_PRIVATE_ADDR_CYCLE_INTERVAL_S). + .private_addr_cycle_s = + timeout == BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED ? 0 : timeout + 1, .p_device_irk = NULL, }; err_code = sd_ble_gap_privacy_set(&privacy); @@ -913,6 +906,7 @@ void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool tx_power, directed_to)); self->user_advertising = true; + self->advertising_started_by_user = true; } void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self) { @@ -998,13 +992,13 @@ void bleio_adapter_reset(bleio_adapter_obj_t *adapter) { // Wait up to 125 ms (128 ticks) for disconnect to complete. This should be // greater than most connection intervals. - bool any_connected = false; + bool any_connected; uint64_t start_ticks = supervisor_ticks_ms64(); - while (any_connected && supervisor_ticks_ms64() - start_ticks < 128) { + do { any_connected = false; for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { bleio_connection_internal_t *connection = &bleio_connections[i]; any_connected |= connection->conn_handle != BLE_CONN_HANDLE_INVALID; } - } + } while (any_connected && supervisor_ticks_ms64() - start_ticks < 128); } diff --git a/ports/nordic/common-hal/_bleio/Adapter.h b/ports/nordic/common-hal/_bleio/Adapter.h index 5bc1805d072..6f776261a9c 100644 --- a/ports/nordic/common-hal/_bleio/Adapter.h +++ b/ports/nordic/common-hal/_bleio/Adapter.h @@ -11,6 +11,7 @@ #include "py/obj.h" #include "py/objtuple.h" +#include "shared-bindings/_bleio/Address.h" #include "shared-bindings/_bleio/Connection.h" #include "shared-bindings/_bleio/ScanResults.h" @@ -38,6 +39,15 @@ typedef struct { ble_drv_evt_handler_entry_t advertising_handler_entry; background_callback_t background_callback; bool user_advertising; + // Whether the current (or most recent) advertising was started by user code + // rather than the supervisor. Unlike user_advertising, this is not cleared when + // advertising stops, so the connected-event handler can still read it: the + // advertising event handler runs first and has already stopped the advertising. + bool advertising_started_by_user; + // Cached local address returned by common_hal_bleio_adapter_get_address(). + // Stored inline so it never needs to allocate, even when read before the + // heap is available (e.g. from bleio_adapter_reset_name). + bleio_address_obj_t address; } bleio_adapter_obj_t; void bleio_adapter_gc_collect(bleio_adapter_obj_t *adapter); diff --git a/ports/nordic/common-hal/_bleio/Characteristic.c b/ports/nordic/common-hal/_bleio/Characteristic.c index 2e6042e48e7..413c97a3d6e 100644 --- a/ports/nordic/common-hal/_bleio/Characteristic.c +++ b/ports/nordic/common-hal/_bleio/Characteristic.c @@ -240,6 +240,8 @@ void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t * }; check_nrf_error(sd_ble_gatts_descriptor_add(self->handle, &desc_attr, &descriptor->handle)); + bleio_gatts_min_handle = MIN(bleio_gatts_min_handle, descriptor->handle); + bleio_gatts_max_handle = MAX(bleio_gatts_max_handle, descriptor->handle); mp_obj_list_append(MP_OBJ_FROM_PTR(self->descriptor_list), MP_OBJ_FROM_PTR(descriptor)); diff --git a/ports/nordic/common-hal/_bleio/Connection.c b/ports/nordic/common-hal/_bleio/Connection.c index f32034582b4..fbd3263b725 100644 --- a/ports/nordic/common-hal/_bleio/Connection.c +++ b/ports/nordic/common-hal/_bleio/Connection.c @@ -289,15 +289,23 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { // mode >=1 and/or level >=1 means encryption is set up self->pair_status = PAIR_NOT_PAIRED; } else { - if (bonding_load_cccd_info(self->is_central, self->conn_handle, self->ediv)) { - // Did an sd_ble_gatts_sys_attr_set() with the stored sys_attr values. - // Indicate ATTR table change because we may have reloaded since the peer last - // connected. - sd_ble_gatts_service_changed(self->conn_handle, 0xC, 0xFFFF); - } else { - // No matching bonding found, so use fresh system attributes. + if (!bonding_load_cccd_info(self->is_central, self->conn_handle, self->ediv)) { + // No stored system attributes (CCCD values) found, so use fresh ones. sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0); } + if (self->ediv != EDIV_INVALID && bleio_gatts_min_handle <= bleio_gatts_max_handle) { + // The peer reconnected using a stored bond, and the GATT table may + // have changed since it last connected: a reload can add or remove + // user services. Indicate Service Changed so the peer discards its + // cached table. This must not be conditional on the sys-attr load + // above succeeding: a peer that missed the indication would trust a + // stale cache indefinitely, and saved system attributes are the + // less reliable of the two records. The error is ignored if the + // peer hasn't subscribed to indications yet. Both handles must be + // within the application-populated range: the SoftDevice rejects + // 0xFFFF, and also 0xC, which is inside its own GATT service. + (void)sd_ble_gatts_service_changed(self->conn_handle, bleio_gatts_min_handle, bleio_gatts_max_handle); + } self->pair_status = PAIR_PAIRED; } break; diff --git a/ports/nordic/common-hal/_bleio/Connection.h b/ports/nordic/common-hal/_bleio/Connection.h index ea1edf17603..78e2ccd5314 100644 --- a/ports/nordic/common-hal/_bleio/Connection.h +++ b/ports/nordic/common-hal/_bleio/Connection.h @@ -31,6 +31,11 @@ typedef enum { typedef struct { uint16_t conn_handle; bool is_central; + // True if user code initiated or accepted this connection: it connected in the + // central role, or a central answered user code's advertising. User-owned + // connections are disconnected when the VM resets; the BLE workflow connection + // is not user-owned and stays up. + bool user_owned; // Remote services discovered when this peripheral is acting as a client. mp_obj_list_t *remote_service_list; // The advertising data and scan response buffers are held by us, not by the SD, so we must diff --git a/ports/nordic/common-hal/_bleio/PacketBuffer.c b/ports/nordic/common-hal/_bleio/PacketBuffer.c index 6b3e86e3b7a..5be9896b706 100644 --- a/ports/nordic/common-hal/_bleio/PacketBuffer.c +++ b/ports/nordic/common-hal/_bleio/PacketBuffer.c @@ -481,8 +481,14 @@ bool common_hal_bleio_packet_buffer_deinited(bleio_packet_buffer_obj_t *self) { void common_hal_bleio_packet_buffer_deinit(bleio_packet_buffer_obj_t *self) { if (!common_hal_bleio_packet_buffer_deinited(self)) { - ble_drv_remove_event_handler(packet_buffer_on_ble_client_evt, self); + if (self->client) { + ble_drv_remove_event_handler(packet_buffer_on_ble_client_evt, self); + } else { + ble_drv_remove_event_handler(packet_buffer_on_ble_server_evt, self); + } ringbuf_deinit(&self->ringbuf); + // Mark as deinited, so common_hal_bleio_packet_buffer_deinited() reports it. + self->characteristic = NULL; } } diff --git a/ports/nordic/common-hal/_bleio/Service.c b/ports/nordic/common-hal/_bleio/Service.c index 1bd75f8a481..c187c54178d 100644 --- a/ports/nordic/common-hal/_bleio/Service.c +++ b/ports/nordic/common-hal/_bleio/Service.c @@ -46,6 +46,8 @@ uint32_t _common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uu uint32_t result = sd_ble_gatts_service_add(service_type, &nordic_uuid, &self->handle); // Do a service changed indication to all connected peers. if (result == NRF_SUCCESS) { + bleio_gatts_min_handle = MIN(bleio_gatts_min_handle, self->handle); + bleio_gatts_max_handle = MAX(bleio_gatts_max_handle, self->handle); _indicate_service_change(self->handle, self->handle); } @@ -169,6 +171,8 @@ void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, _expand_range(char_handles.cccd_handle, &start, &end); _expand_range(char_handles.sccd_handle, &start, &end); _expand_range(char_handles.user_desc_handle, &start, &end); + bleio_gatts_min_handle = MIN(bleio_gatts_min_handle, start); + bleio_gatts_max_handle = MAX(bleio_gatts_max_handle, end); _indicate_service_change(start, end); #if CIRCUITPY_VERBOSE_BLE diff --git a/ports/nordic/common-hal/_bleio/__init__.c b/ports/nordic/common-hal/_bleio/__init__.c index 9dc58d7687c..554bbf200bc 100644 --- a/ports/nordic/common-hal/_bleio/__init__.c +++ b/ports/nordic/common-hal/_bleio/__init__.c @@ -70,13 +70,16 @@ void check_sec_status(uint8_t sec_status) { switch (sec_status) { case BLE_GAP_SEC_STATUS_UNSPECIFIED: - mp_raise_bleio_SecurityError(MP_ERROR_TEXT("Unspecified issue. Can be that the pairing prompt on the other device was declined or ignored.")); + mp_raise_bleio_SecurityError(MP_ERROR_TEXT("Unspecified issue. The pairing request on the other device may have been declined or ignored.")); return; default: mp_raise_bleio_SecurityError(MP_ERROR_TEXT("Unknown security error: 0x%04x"), sec_status); } } +uint16_t bleio_gatts_min_handle = 0xFFFF; +uint16_t bleio_gatts_max_handle; + void common_hal_bleio_init(void) { } @@ -85,6 +88,30 @@ void bleio_user_reset(void) { // Stop any user scanning or advertising. common_hal_bleio_adapter_stop_scan(&common_hal_bleio_adapter_obj); common_hal_bleio_adapter_stop_advertising(&common_hal_bleio_adapter_obj); + + // Disconnect the connections that user code initiated or accepted with its + // own advertising. The BLE workflow connection is not user-owned and stays up. + // + // Clear each connection's pointers into the VM heap first: the heap is about + // to go away, and a disconnect completes asynchronously, possibly after it + // has. The disconnect event handler dereferences connection_obj unless it is + // mp_const_none. + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connection_internal_t *connection = &bleio_connections[i]; + connection->connection_obj = mp_const_none; + connection->remote_service_list = NULL; + if (connection->conn_handle != BLE_CONN_HANDLE_INVALID && connection->user_owned) { + common_hal_bleio_connection_disconnect(connection); + } + } + + // Clear the adapter's remaining pointers into the VM heap. The full stack + // reset in bleio_reset() used to do this implicitly, but it no longer always + // runs. The adapter struct is a GC root, so a pointer left over from this + // heap would be scanned as a live object in the next VM's heap. + common_hal_bleio_adapter_obj.connection_objs = NULL; + common_hal_bleio_adapter_obj.advertising_data = NULL; + common_hal_bleio_adapter_obj.scan_response_data = NULL; } ble_drv_remove_heap_handlers(); @@ -101,6 +128,16 @@ void bleio_reset(void) { return; } + // The SoftDevice cannot remove individual services from its GATT table: the + // disable/enable cycle below is the only way to clear them, and it drops every + // connection, the BLE workflow's included. So run it only when user code created + // services. All other user BLE state has already been torn down individually by + // bleio_user_reset(). + if (!bleio_get_user_services_created()) { + return; + } + bleio_clear_user_services_created(); + supervisor_stop_bluetooth(); bleio_adapter_reset(&common_hal_bleio_adapter_obj); common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, false); diff --git a/ports/nordic/common-hal/_bleio/__init__.h b/ports/nordic/common-hal/_bleio/__init__.h index caf287f9204..eecbc32275f 100644 --- a/ports/nordic/common-hal/_bleio/__init__.h +++ b/ports/nordic/common-hal/_bleio/__init__.h @@ -8,6 +8,12 @@ void bleio_background(void); +// Remember the range of attribute handles the application has populated in the +// SoftDevice's GATT table. Required to call sd_ble_gatts_service_changed() +// with valid arguments: it fails if the args are outside that range. +extern uint16_t bleio_gatts_min_handle; +extern uint16_t bleio_gatts_max_handle; + typedef struct { ble_gap_enc_key_t own_enc; ble_gap_enc_key_t peer_enc; diff --git a/ports/nordic/common-hal/alarm/pin/PinAlarm.c b/ports/nordic/common-hal/alarm/pin/PinAlarm.c index f43eb1ed251..3359faa74e0 100644 --- a/ports/nordic/common-hal/alarm/pin/PinAlarm.c +++ b/ports/nordic/common-hal/alarm/pin/PinAlarm.c @@ -17,7 +17,9 @@ #include "nrfx.h" #include "nrf_gpio.h" #include "nrfx_gpiote.h" +#ifdef BLUETOOTH_SD #include "nrf_soc.h" +#endif #include #define WPIN_UNUSED 0xFF diff --git a/ports/nordic/common-hal/analogio/AnalogIn.c b/ports/nordic/common-hal/analogio/AnalogIn.c index e48c9dd2e28..1459c67c08c 100644 --- a/ports/nordic/common-hal/analogio/AnalogIn.c +++ b/ports/nordic/common-hal/analogio/AnalogIn.c @@ -23,6 +23,9 @@ void analogin_init(void) { while (nrf_saadc_event_check(NRF_SAADC, NRF_SAADC_EVENT_CALIBRATEDONE) == 0) { } nrf_saadc_event_clear(NRF_SAADC, NRF_SAADC_EVENT_CALIBRATEDONE); + nrf_saadc_event_clear(NRF_SAADC, NRF_SAADC_EVENT_END); + nrf_saadc_event_clear(NRF_SAADC, NRF_SAADC_EVENT_DONE); + nrf_saadc_event_clear(NRF_SAADC, NRF_SAADC_EVENT_RESULTDONE); nrf_saadc_disable(NRF_SAADC); } @@ -56,7 +59,8 @@ uint16_t common_hal_analogio_analogin_get_value(analogio_analogin_obj_t *self) { // Something else might have used the ADC in a different way, // so we completely re-initialize it. - nrf_saadc_value_t value = 0; + static volatile nrf_saadc_value_t value __attribute__((aligned(4))); + value = 0; const nrf_saadc_channel_config_t config = { .resistor_p = NRF_SAADC_RESISTOR_DISABLED, @@ -78,7 +82,12 @@ uint16_t common_hal_analogio_analogin_get_value(analogio_analogin_obj_t *self) { nrf_saadc_channel_init(NRF_SAADC, CHANNEL_NO, &config); nrf_saadc_channel_input_set(NRF_SAADC, CHANNEL_NO, self->pin->adc_channel, self->pin->adc_channel); - nrf_saadc_buffer_init(NRF_SAADC, &value, 1); + nrf_saadc_buffer_init(NRF_SAADC, (nrf_saadc_value_t *)&value, 1); + nrf_saadc_event_clear(NRF_SAADC, NRF_SAADC_EVENT_STARTED); + nrf_saadc_event_clear(NRF_SAADC, NRF_SAADC_EVENT_END); + nrf_saadc_event_clear(NRF_SAADC, NRF_SAADC_EVENT_DONE); + nrf_saadc_event_clear(NRF_SAADC, NRF_SAADC_EVENT_RESULTDONE); + nrf_saadc_event_clear(NRF_SAADC, NRF_SAADC_EVENT_STOPPED); nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_START); while (nrf_saadc_event_check(NRF_SAADC, NRF_SAADC_EVENT_STARTED) == 0) { @@ -100,23 +109,20 @@ uint16_t common_hal_analogio_analogin_get_value(analogio_analogin_obj_t *self) { nrf_saadc_disable(NRF_SAADC); - // Adding the "asm volatile" memory fence here or anywhere after the declaration of `value` - // fixes an issue with gcc13 which causes `value` to always be zero. - // Compiling with gcc10 or gcc12 is fine. - // It can also be fixed by declaring `value` to be static. - // I think I'd like to declare `value` as volatile, but that causes type errors. - asm volatile ("" : : : "memory"); // Disconnect ADC from pin. nrf_saadc_channel_input_set(NRF_SAADC, CHANNEL_NO, NRF_SAADC_INPUT_DISABLED, NRF_SAADC_INPUT_DISABLED); + // Read the DMA'd result exactly once. + int32_t result = value; + // value is signed and might be (slightly) < 0, even on single-ended conversions, so force to 0. - if (value < 0) { - value = 0; + if (result < 0) { + result = 0; } // Stretch 14-bit ADC reading to 16-bit range - return (value << 2) | (value >> 12); + return (result << 2) | (result >> 12); } float common_hal_analogio_analogin_get_reference_voltage(analogio_analogin_obj_t *self) { diff --git a/ports/nordic/common-hal/audiobusio/I2SOut.c b/ports/nordic/common-hal/audiobusio/I2SOut.c index fbdaa93cfa4..46b9279cbcc 100644 --- a/ports/nordic/common-hal/audiobusio/I2SOut.c +++ b/ports/nordic/common-hal/audiobusio/I2SOut.c @@ -16,6 +16,8 @@ #include "py/obj.h" #include "py/runtime.h" +#include "nrf_gpio.h" + static audiobusio_i2sout_obj_t *instance; struct { int16_t l, r; @@ -191,9 +193,6 @@ void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t *self, const mcu_pin_obj_t *bit_clock, const mcu_pin_obj_t *word_select, const mcu_pin_obj_t *data, const mcu_pin_obj_t *main_clock, bool left_justified, bool external_clock) { - if (external_clock) { - mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_external_clock); - } if (main_clock != NULL) { mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_main_clock); } @@ -206,14 +205,23 @@ void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t *self, claim_pin(word_select); claim_pin(data); + self->external_clock = external_clock; + NRF_I2S->PSEL.SCK = self->bit_clock_pin_number = bit_clock->number; NRF_I2S->PSEL.LRCK = self->word_select_pin_number = word_select->number; NRF_I2S->PSEL.SDOUT = self->data_pin_number = data->number; - NRF_I2S->CONFIG.MODE = I2S_CONFIG_MODE_MODE_Master; + if (external_clock) { + nrf_gpio_cfg_input(bit_clock->number, NRF_GPIO_PIN_NOPULL); + nrf_gpio_cfg_input(word_select->number, NRF_GPIO_PIN_NOPULL); + } + + NRF_I2S->CONFIG.MODE = external_clock ? I2S_CONFIG_MODE_MODE_Slave + : I2S_CONFIG_MODE_MODE_Master; NRF_I2S->CONFIG.RXEN = I2S_CONFIG_RXEN_RXEN_Disabled; NRF_I2S->CONFIG.TXEN = I2S_CONFIG_TXEN_TXEN_Enabled; - NRF_I2S->CONFIG.MCKEN = I2S_CONFIG_MCKEN_MCKEN_Enabled; + NRF_I2S->CONFIG.MCKEN = external_clock ? I2S_CONFIG_MCKEN_MCKEN_Disabled + : I2S_CONFIG_MCKEN_MCKEN_Enabled; NRF_I2S->CONFIG.SWIDTH = I2S_CONFIG_SWIDTH_SWIDTH_16Bit; NRF_I2S->CONFIG.ALIGN = I2S_CONFIG_ALIGN_ALIGN_Left; @@ -233,6 +241,10 @@ void common_hal_audiobusio_i2sout_deinit(audiobusio_i2sout_obj_t *self) { } NRF_I2S->TASKS_STOP = 1; NRF_I2S->ENABLE = I2S_ENABLE_ENABLE_Disabled; + if (self->external_clock) { + nrf_gpio_cfg_default(self->bit_clock_pin_number); + nrf_gpio_cfg_default(self->word_select_pin_number); + } reset_pin_number(self->bit_clock_pin_number); self->bit_clock_pin_number = NO_PIN; reset_pin_number(self->word_select_pin_number); @@ -272,7 +284,11 @@ void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t *self, ? I2S_CONFIG_CHANNELS_CHANNELS_Left : I2S_CONFIG_CHANNELS_CHANNELS_Stereo; - choose_i2s_clocking(self, sample_rate); + if (self->external_clock) { + self->sample_rate = sample_rate; + } else { + choose_i2s_clocking(self, sample_rate); + } /* Allocate buffers based on a maximum duration * This duration was chosen empirically based on what would * cause os.listdir('') to cause stuttering. It seems like a diff --git a/ports/nordic/common-hal/audiobusio/I2SOut.h b/ports/nordic/common-hal/audiobusio/I2SOut.h index 33ec917eb64..27e077c0649 100644 --- a/ports/nordic/common-hal/audiobusio/I2SOut.h +++ b/ports/nordic/common-hal/audiobusio/I2SOut.h @@ -28,6 +28,7 @@ typedef struct { uint8_t bytes_per_sample; bool left_justified : 1; + bool external_clock : 1; bool playing : 1; bool stopping : 1; bool last_buffer : 1; diff --git a/ports/nordic/common-hal/busio/UART.c b/ports/nordic/common-hal/busio/UART.c index 6939486a5e6..7a4219a9728 100644 --- a/ports/nordic/common-hal/busio/UART.c +++ b/ports/nordic/common-hal/busio/UART.c @@ -182,7 +182,8 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, .interrupt_priority = NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY, .hal_cfg = { .hwfc = hwfc ? NRF_UARTE_HWFC_ENABLED : NRF_UARTE_HWFC_DISABLED, - .parity = (parity == BUSIO_UART_PARITY_NONE) ? NRF_UARTE_PARITY_EXCLUDED : NRF_UARTE_PARITY_INCLUDED + .parity = (parity == BUSIO_UART_PARITY_NONE) ? NRF_UARTE_PARITY_EXCLUDED : NRF_UARTE_PARITY_INCLUDED, + .stop = (stop == 2 ? NRF_UARTE_STOP_TWO : NRF_UARTE_STOP_ONE), } }; diff --git a/ports/nordic/common-hal/microcontroller/__init__.c b/ports/nordic/common-hal/microcontroller/__init__.c index fbb40afe044..d5752fb46b6 100644 --- a/ports/nordic/common-hal/microcontroller/__init__.c +++ b/ports/nordic/common-hal/microcontroller/__init__.c @@ -24,7 +24,9 @@ #include "supervisor/port.h" #include "supervisor/shared/safe_mode.h" #include "nrfx_glue.h" +#ifdef BLUETOOTH_SD #include "nrf_nvic.h" +#endif #include "nrf_power.h" // This routine should work even when interrupts are disabled. Used by OneWire @@ -45,7 +47,14 @@ void common_hal_mcu_disable_interrupts(void) { // This only disables interrupts of priority 2 through 7; levels 0, 1, // and 4, are exclusive to softdevice and should never be used, so // this limitation is not important. + #ifdef BLUETOOTH_SD sd_nvic_critical_region_enter(&is_nested_critical_region); + #else + // With no SoftDevice to leave room for, mask everything. Record whether + // interrupts were already masked so the matching exit leaves them so. + is_nested_critical_region = __get_PRIMASK() ? 1 : 0; + __disable_irq(); + #endif } __DMB(); nesting_count++; @@ -61,7 +70,13 @@ void common_hal_mcu_enable_interrupts(void) { return; } __DMB(); + #ifdef BLUETOOTH_SD sd_nvic_critical_region_exit(is_nested_critical_region); + #else + if (!is_nested_critical_region) { + __enable_irq(); + } + #endif } void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) { @@ -70,11 +85,16 @@ void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) { if (runmode == RUNMODE_BOOTLOADER || runmode == RUNMODE_UF2) { new_value = DFU_MAGIC_UF2_RESET; } + #ifdef BLUETOOTH_SD int err_code = sd_power_gpregret_set(0, DFU_MAGIC_UF2_RESET); if (err_code != NRF_SUCCESS) { // Set it without the soft device if the SD failed. (It may be off.) nrf_power_gpregret_set(NRF_POWER, new_value); } + #else + // No SoftDevice, so write GPREGRET directly. + nrf_power_gpregret_set(NRF_POWER, new_value); + #endif if (runmode == RUNMODE_SAFE_MODE) { safe_mode_on_next_reset(SAFE_MODE_PROGRAMMATIC); } diff --git a/ports/nordic/common-hal/neopixel_write/__init__.c b/ports/nordic/common-hal/neopixel_write/__init__.c index 66a1d70e869..e9f3a0fdacc 100644 --- a/ports/nordic/common-hal/neopixel_write/__init__.c +++ b/ports/nordic/common-hal/neopixel_write/__init__.c @@ -125,7 +125,9 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t *digitalinout, pixels_pattern = (uint16_t *)stack_pixels; } else { uint8_t sd_en = 0; + #ifdef BLUETOOTH_SD (void)sd_softdevice_is_enabled(&sd_en); + #endif if (pixels_pattern_heap_size < pattern_size) { // Current heap buffer is too small. diff --git a/ports/nordic/device/nrf52/startup_nrf52840.c b/ports/nordic/device/nrf52/startup_nrf52840.c index 6b81fac2a9c..f602a86e079 100644 --- a/ports/nordic/device/nrf52/startup_nrf52840.c +++ b/ports/nordic/device/nrf52/startup_nrf52840.c @@ -27,8 +27,23 @@ void Default_Handler(void) { } } +extern const func __Vectors[]; + +#ifndef BLUETOOTH_SD +// SCB->VTOR, addressed directly so the startup file need not pull in CMSIS. +#define SCB_VTOR (*(volatile uint32_t *)0xE000ED08UL) +#endif + extern void Reset_Handler(void); void Reset_Handler(void) { + #ifndef BLUETOOTH_SD + // With a SoftDevice, the MBR at 0x0 owns the vector table and forwards + // interrupts to us, so VTOR is already pointing at our table by the time we + // run. + SCB_VTOR = (uint32_t)&__Vectors[0]; + __asm volatile ("dsb 0xF" ::: "memory"); + #endif + uint32_t *p_src = &_sidata; uint32_t *p_dest = &_sdata; diff --git a/ports/nordic/mpconfigport.h b/ports/nordic/mpconfigport.h index 33fcfa371e0..d926da73596 100644 --- a/ports/nordic/mpconfigport.h +++ b/ports/nordic/mpconfigport.h @@ -7,10 +7,13 @@ #pragma once +#ifdef BLUETOOTH_SD #include "ble_drv.h" #include "nrf_mbr.h" // for MBR_SIZE #include "nrf_sdm.h" // for SD_FLASH_SIZE +#endif + #include "peripherals/nrf/nvm.h" // for FLASH_PAGE_SIZE #define MICROPY_PY_SYS_STDIO_BUFFER (1) @@ -44,6 +47,17 @@ // Definitions that might be overridden by mpconfigboard.h +#ifndef BLUETOOTH_SD + +#ifndef MBR_SIZE +#define MBR_SIZE (0x1000) +#endif + +#ifndef SD_FLASH_SIZE +#define SD_FLASH_SIZE (0) +#endif +#endif + #ifndef CIRCUITPY_INTERNAL_NVM_SIZE #define CIRCUITPY_INTERNAL_NVM_SIZE (8 * 1024) #endif @@ -159,7 +173,12 @@ // high enough to work and then check the mutation of the value done by sd_ble_enable(). // See common.template.ld. #ifndef SOFTDEVICE_RAM_SIZE +#ifdef BLUETOOTH_SD #define SOFTDEVICE_RAM_SIZE (56 * 1024) +#else + +#define SOFTDEVICE_RAM_SIZE (0) +#endif #endif diff --git a/ports/nordic/mpconfigport.mk b/ports/nordic/mpconfigport.mk index 028ceca9fcd..a9299ee60aa 100644 --- a/ports/nordic/mpconfigport.mk +++ b/ports/nordic/mpconfigport.mk @@ -124,3 +124,8 @@ ifeq ($(INTERNAL_FLASH_FILESYSTEM),1) endif endif endif + +ifneq ($(CIRCUITPY_BLEIO_NATIVE),1) +CIRCUITPY_BLE_FILE_SERVICE = 0 +CIRCUITPY_BLE_SERIAL_SERVICE = 0 +endif diff --git a/ports/nordic/peripherals/nrf/nvm.h b/ports/nordic/peripherals/nrf/nvm.h index aff98094469..10bf92d90cf 100644 --- a/ports/nordic/peripherals/nrf/nvm.h +++ b/ports/nordic/peripherals/nrf/nvm.h @@ -7,9 +7,12 @@ #pragma once +#include +#include + #define FLASH_PAGE_SIZE (4096) -#if BLUETOOTH_SD +#ifdef BLUETOOTH_SD bool sd_flash_page_erase_sync(uint32_t page_number); bool sd_flash_write_sync(uint32_t *dest_words, uint32_t *src_words, uint32_t num_words); #endif diff --git a/ports/nordic/supervisor/port.c b/ports/nordic/supervisor/port.c index 1eabfcbe216..0bb23364081 100644 --- a/ports/nordic/supervisor/port.c +++ b/ports/nordic/supervisor/port.c @@ -22,7 +22,9 @@ #include "nrf/power.h" #include "nrf/timers.h" +#ifdef BLUETOOTH_SD #include "nrf_nvic.h" +#endif #include "common-hal/microcontroller/Pin.h" #include "common-hal/alarm/time/TimeAlarm.h" @@ -34,7 +36,6 @@ #include "common-hal/watchdog/WatchDogTimer.h" #include "common-hal/alarm/__init__.h" -#include "shared-bindings/_bleio/__init__.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/rtc/__init__.h" @@ -323,6 +324,7 @@ void port_idle_until_interrupt(void) { (void)__get_FPSCR(); NVIC_ClearPendingIRQ(FPU_IRQn); } + #ifdef BLUETOOTH_SD uint8_t sd_enabled; sd_softdevice_is_enabled(&sd_enabled); @@ -330,7 +332,10 @@ void port_idle_until_interrupt(void) { if (!background_callback_pending()) { sd_app_evt_wait(); } - } else { + return; + } + #endif + { // Call wait for interrupt ourselves if the SD isn't enabled. // Note that `wfi` should be called with interrupts disabled, // to ensure that the queue is properly drained. The `wfi` diff --git a/ports/raspberrypi/Makefile b/ports/raspberrypi/Makefile old mode 100644 new mode 100755 index 5dc7768c2cf..1c13d538e51 --- a/ports/raspberrypi/Makefile +++ b/ports/raspberrypi/Makefile @@ -84,6 +84,33 @@ SRC_CYW43 := SRC_LWIP := endif +ifeq ($(CIRCUITPY_WIZNET), 1) +INC_WIZNET := \ + -isystem common-hal/wiznet \ + +SRC_WIZNET := \ + common-hal/wiznet/wizchip_pio_spi.c \ + +WIZNET_PIOASM = $(BUILD)/pioasm/pioasm/pioasm +.PHONY: pioasmBuild +pioasmBuild: $(WIZNET_PIOASM) + +$(WIZNET_PIOASM): + $(Q)cmake -S pioasm -B $(BUILD)/pioasm + $(MAKE) -C $(BUILD)/pioasm pioasmBuild + +$(BUILD)/wizchip_pio_spi.pio.h: common-hal/wiznet/wizchip_pio_spi.pio $(WIZNET_PIOASM) + $(Q)$(WIZNET_PIOASM) -o c-sdk $< $@ +$(BUILD)/common-hal/wiznet/wizchip_pio_spi.o: $(BUILD)/wizchip_pio_spi.pio.h + +$(BUILD)/genhdr/qstr.i.last: $(BUILD)/wizchip_pio_spi.pio.h + +else +INC_WIZNET := +SRC_WIZNET := + +endif + CHIP_VARIANT_LOWER = $(shell echo $(CHIP_VARIANT) | tr '[:upper:]' '[:lower:]') INC += \ @@ -480,6 +507,13 @@ endif SRC_C += shared/runtime/gchelper_native.c +ifeq ($(CIRCUITPY_PICOGAME),1) +# SIO interpolator fast path for the picogame mode7 inner row (RP2040 + RP2350). +# Portable C remains the fallback on other ports. +SRC_C += common-hal/picogame/interp.c +CFLAGS += -DPICOGAME_HAS_INTERP=1 +endif + SRC_SDK := \ src/common/hardware_claim/claim.c \ src/common/pico_sync/critical_section.c \ @@ -541,6 +575,10 @@ SRC_C += \ bindings/rp2pio/__init__.c \ common-hal/rp2pio/StateMachine.c \ common-hal/rp2pio/__init__.c \ + bindings/wiznet/PIO_SPI.c \ + bindings/wiznet/__init__.c \ + common-hal/wiznet/PIO_SPI.c \ + common-hal/wiznet/__init__.c \ audio_dma.c \ background.c \ peripherals/pins.c \ @@ -550,6 +588,7 @@ SRC_C += \ mphalport.c \ $(SRC_CYW43) \ $(SRC_LWIP) \ + $(SRC_WIZNET) \ ifeq ($(CIRCUITPY_USB_HOST), 1) diff --git a/ports/raspberrypi/bindings/picodvi/Framebuffer.c b/ports/raspberrypi/bindings/picodvi/Framebuffer.c index 9cd66ad7e2a..e80ffea470c 100644 --- a/ports/raspberrypi/bindings/picodvi/Framebuffer.c +++ b/ports/raspberrypi/bindings/picodvi/Framebuffer.c @@ -189,9 +189,27 @@ MP_DEFINE_CONST_FUN_OBJ_1(picodvi_framebuffer_get_color_depth_obj, picodvi_frame MP_PROPERTY_GETTER(picodvi_framebuffer_color_depth_obj, (mp_obj_t)&picodvi_framebuffer_get_color_depth_obj); +//| def wait_for_vblank(self) -> None: +//| """Block until the next frame boundary (vertical blank), then return. Call this right +//| before a full-framebuffer repaint so the repaint starts at the top of scanout and races +//| ahead of the beam, avoiding tearing without a second framebuffer. The repaint must +//| complete faster than one scanout sweep (~16 ms at 60 Hz) to stay ahead of the beam; +//| if it cannot (e.g. a full 640x480 ``displayio`` refresh), use a lower resolution or +//| repaint a smaller area. Bounded by a short timeout, so it is safe even if no DVI +//| signal is active. No-op on the RP2040 PIO-DVI path.""" +//| ... +static mp_obj_t picodvi_framebuffer_wait_for_vblank(mp_obj_t self_in) { + picodvi_framebuffer_obj_t *self = (picodvi_framebuffer_obj_t *)self_in; + check_for_deinit(self); + common_hal_picodvi_framebuffer_wait_for_vblank(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(picodvi_framebuffer_wait_for_vblank_obj, picodvi_framebuffer_wait_for_vblank); + static const mp_rom_map_elem_t picodvi_framebuffer_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&picodvi_framebuffer_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_wait_for_vblank), MP_ROM_PTR(&picodvi_framebuffer_wait_for_vblank_obj) }, { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&picodvi_framebuffer_width_obj) }, { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&picodvi_framebuffer_height_obj) }, diff --git a/ports/raspberrypi/bindings/picodvi/Framebuffer.h b/ports/raspberrypi/bindings/picodvi/Framebuffer.h index 7eb7780ee49..d5f59fe84c3 100644 --- a/ports/raspberrypi/bindings/picodvi/Framebuffer.h +++ b/ports/raspberrypi/bindings/picodvi/Framebuffer.h @@ -25,6 +25,7 @@ void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self, void common_hal_picodvi_framebuffer_deinit(picodvi_framebuffer_obj_t *self); bool common_hal_picodvi_framebuffer_deinited(picodvi_framebuffer_obj_t *self); void common_hal_picodvi_framebuffer_refresh(picodvi_framebuffer_obj_t *self); +void common_hal_picodvi_framebuffer_wait_for_vblank(picodvi_framebuffer_obj_t *self); int common_hal_picodvi_framebuffer_get_width(picodvi_framebuffer_obj_t *self); int common_hal_picodvi_framebuffer_get_height(picodvi_framebuffer_obj_t *self); int common_hal_picodvi_framebuffer_get_row_stride(picodvi_framebuffer_obj_t *self); diff --git a/ports/raspberrypi/bindings/wiznet/PIO_SPI.c b/ports/raspberrypi/bindings/wiznet/PIO_SPI.c new file mode 100755 index 00000000000..1f860fc9979 --- /dev/null +++ b/ports/raspberrypi/bindings/wiznet/PIO_SPI.c @@ -0,0 +1,299 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft +// +// SPDX-License-Identifier: MIT + +// TODO: wiznet.PIO_SPI class. +// This file contains all of the Python API definitions for the +// wiznet.PIO_SPI class. + +#include + +#include "shared-bindings/microcontroller/Pin.h" +#include "bindings/wiznet/PIO_SPI.h" +#include "shared-bindings/util.h" + +#include "shared/runtime/buffer_helper.h" +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/mperrno.h" +#include "py/objproperty.h" +#include "py/runtime.h" + + +// TODO: class WIZNET_PIO_SPI + + +static mp_obj_t wiznet_pio_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + #if CIRCUITPY_WIZNET + wiznet_pio_spi_obj_t *self = mp_obj_malloc(wiznet_pio_spi_obj_t, &wiznet_pio_spi_type); + enum { ARG_clock, ARG_MOSI, ARG_MISO, ARG_half_duplex }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_clock, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_MOSI, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_MISO, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_half_duplex, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj, MP_QSTR_clock); + const mcu_pin_obj_t *mosi = validate_obj_is_free_pin_or_none(args[ARG_MOSI].u_obj, MP_QSTR_mosi); + const mcu_pin_obj_t *miso = validate_obj_is_free_pin_or_none(args[ARG_MISO].u_obj, MP_QSTR_miso); + + if (!miso && !mosi) { + mp_raise_ValueError(MP_ERROR_TEXT("Must provide MISO or MOSI pin")); + } + + common_hal_wiznet_pio_spi_construct(self, clock, mosi, miso, args[ARG_half_duplex].u_bool); + return MP_OBJ_FROM_PTR(self); + #else + mp_raise_NotImplementedError(NULL); + #endif // CIRCUITPY_WIZNET +} + +#if CIRCUITPY_WIZNET + +// TODO: def deinit + +static mp_obj_t wiznet_pio_spi_obj_deinit(mp_obj_t self_in) { + wiznet_pio_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_wiznet_pio_spi_deinit(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(wiznet_pio_spi_deinit_obj, wiznet_pio_spi_obj_deinit); + +// TODO: def __enter__ + +// TODO: def __exit__ + +static void check_lock(wiznet_pio_spi_obj_t *self) { + asm (""); + if (!common_hal_wiznet_pio_spi_has_lock(self)) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Function requires lock")); + } +} + +static void check_for_deinit(wiznet_pio_spi_obj_t *self) { + if (common_hal_wiznet_pio_spi_deinited(self)) { + raise_deinited_error(); + } +} + +// TODO: def configure + +static mp_obj_t wiznet_pio_spi_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100000} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, + }; + wiznet_pio_spi_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + check_lock(self); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + uint8_t polarity = (uint8_t)mp_arg_validate_int_range(args[ARG_polarity].u_int, 0, 1, MP_QSTR_polarity); + uint8_t phase = (uint8_t)mp_arg_validate_int_range(args[ARG_phase].u_int, 0, 1, MP_QSTR_phase); + uint8_t bits = (uint8_t)mp_arg_validate_int_range(args[ARG_bits].u_int, 8, 9, MP_QSTR_bits); + + if (!common_hal_wiznet_pio_spi_configure(self, args[ARG_baudrate].u_int, + polarity, phase, bits)) { + mp_raise_OSError(MP_EIO); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(wiznet_pio_spi_configure_obj, 1, wiznet_pio_spi_configure); + +// TODO: def try_lock + +static mp_obj_t wiznet_pio_spi_obj_try_lock(mp_obj_t self_in) { + wiznet_pio_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_wiznet_pio_spi_try_lock(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(wiznet_pio_spi_try_lock_obj, wiznet_pio_spi_obj_try_lock); + +// TODO: def unlock + +static mp_obj_t wiznet_pio_spi_obj_unlock(mp_obj_t self_in) { + wiznet_pio_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_wiznet_pio_spi_unlock(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(wiznet_pio_spi_unlock_obj, wiznet_pio_spi_obj_unlock); + +// TODO: def write + +static mp_obj_t wiznet_pio_spi_write(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_buffer, ARG_start, ARG_end }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} }, + }; + + wiznet_pio_spi_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + check_lock(self); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ); + // Compute bounds in terms of elements, not bytes. + int stride_in_bytes = mp_binary_get_size('@', bufinfo.typecode, NULL); + int32_t start = args[ARG_start].u_int; + size_t length = bufinfo.len / stride_in_bytes; + normalize_buffer_bounds(&start, args[ARG_end].u_int, &length); + + // Treat start and length in terms of bytes from now on. + start *= stride_in_bytes; + length *= stride_in_bytes; + + if (length == 0) { + return mp_const_none; + } + + bool ok = common_hal_wiznet_pio_spi_write(self, ((uint8_t *)bufinfo.buf) + start, length); + + if (!ok) { + mp_raise_OSError(MP_EIO); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(wiznet_pio_spi_write_obj, 1, wiznet_pio_spi_write); + +// TODO: def readinto + +static mp_obj_t wiznet_pio_spi_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_buffer, ARG_start, ARG_end, ARG_write_value }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} }, + { MP_QSTR_write_value, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + }; + wiznet_pio_spi_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + check_lock(self); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_WRITE); + // Compute bounds in terms of elements, not bytes. + int stride_in_bytes = mp_binary_get_size('@', bufinfo.typecode, NULL); + int32_t start = args[ARG_start].u_int; + size_t length = bufinfo.len / stride_in_bytes; + normalize_buffer_bounds(&start, args[ARG_end].u_int, &length); + + // Treat start and length in terms of bytes from now on. + start *= stride_in_bytes; + length *= stride_in_bytes; + + if (length == 0) { + return mp_const_none; + } + + bool ok = common_hal_wiznet_pio_spi_read(self, ((uint8_t *)bufinfo.buf) + start, length, args[ARG_write_value].u_int); + if (!ok) { + mp_raise_OSError(MP_EIO); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(wiznet_pio_spi_readinto_obj, 1, wiznet_pio_spi_readinto); + + +// TODO: def write_readinto + +static mp_obj_t wiznet_pio_spi_write_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_out_buffer, ARG_in_buffer, ARG_out_start, ARG_out_end, ARG_in_start, ARG_in_end }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_out_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_in_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_out_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_out_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} }, + { MP_QSTR_in_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_in_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} }, + }; + wiznet_pio_spi_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + check_lock(self); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_buffer_info_t buf_out_info; + mp_get_buffer_raise(args[ARG_out_buffer].u_obj, &buf_out_info, MP_BUFFER_READ); + int out_stride_in_bytes = mp_binary_get_size('@', buf_out_info.typecode, NULL); + int32_t out_start = args[ARG_out_start].u_int; + size_t out_length = buf_out_info.len / out_stride_in_bytes; + normalize_buffer_bounds(&out_start, args[ARG_out_end].u_int, &out_length); + + mp_buffer_info_t buf_in_info; + mp_get_buffer_raise(args[ARG_in_buffer].u_obj, &buf_in_info, MP_BUFFER_WRITE); + int in_stride_in_bytes = mp_binary_get_size('@', buf_in_info.typecode, NULL); + int32_t in_start = args[ARG_in_start].u_int; + size_t in_length = buf_in_info.len / in_stride_in_bytes; + normalize_buffer_bounds(&in_start, args[ARG_in_end].u_int, &in_length); + + // Treat start and length in terms of bytes from now on. + out_start *= out_stride_in_bytes; + out_length *= out_stride_in_bytes; + in_start *= in_stride_in_bytes; + in_length *= in_stride_in_bytes; + + if (out_length != in_length) { + mp_raise_ValueError(MP_ERROR_TEXT("buffer slices must be of equal length")); + } + + if (out_length == 0) { + return mp_const_none; + } + + bool ok = common_hal_wiznet_pio_spi_transfer(self, + ((uint8_t *)buf_out_info.buf) + out_start, + ((uint8_t *)buf_in_info.buf) + in_start, + out_length); + if (!ok) { + mp_raise_OSError(MP_EIO); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(wiznet_pio_spi_write_readinto_obj, 1, wiznet_pio_spi_write_readinto); + +#endif // CIRCUITPY_WIZNET + +static const mp_rom_map_elem_t wiznet_pio_spi_locals_dict_table[] = { + #if CIRCUITPY_WIZNET + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&wiznet_pio_spi_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, + + { MP_ROM_QSTR(MP_QSTR_configure), MP_ROM_PTR(&wiznet_pio_spi_configure_obj) }, + { MP_ROM_QSTR(MP_QSTR_try_lock), MP_ROM_PTR(&wiznet_pio_spi_try_lock_obj) }, + { MP_ROM_QSTR(MP_QSTR_unlock), MP_ROM_PTR(&wiznet_pio_spi_unlock_obj) }, + + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&wiznet_pio_spi_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&wiznet_pio_spi_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&wiznet_pio_spi_write_readinto_obj) }, + + #endif // CIRCUITPY_WIZNET +}; +static MP_DEFINE_CONST_DICT(wiznet_pio_spi_locals_dict, wiznet_pio_spi_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + wiznet_pio_spi_type, + MP_QSTR_PIO_SPI, + MP_TYPE_FLAG_NONE, + make_new, wiznet_pio_spi_make_new, + locals_dict, &wiznet_pio_spi_locals_dict + ); + +wiznet_pio_spi_obj_t *validate_obj_is_wiznet_pio_spi_bus(mp_obj_t obj, qstr arg_name) { + return mp_arg_validate_type(obj, &wiznet_pio_spi_type, arg_name); +} diff --git a/ports/raspberrypi/bindings/wiznet/PIO_SPI.h b/ports/raspberrypi/bindings/wiznet/PIO_SPI.h new file mode 100755 index 00000000000..0289ac0767a --- /dev/null +++ b/ports/raspberrypi/bindings/wiznet/PIO_SPI.h @@ -0,0 +1,40 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/wiznet/PIO_SPI.h" + +// Type object used in Python. Should be shared between ports. +extern const mp_obj_type_t wiznet_pio_spi_type; + +// Construct an underlying SPI object. +extern void common_hal_wiznet_pio_spi_construct(wiznet_pio_spi_obj_t *self, + const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, + const mcu_pin_obj_t *miso, bool half_duplex); + +extern void common_hal_wiznet_pio_spi_deinit(wiznet_pio_spi_obj_t *self); +extern bool common_hal_wiznet_pio_spi_deinited(wiznet_pio_spi_obj_t *self); + +extern bool common_hal_wiznet_pio_spi_configure(wiznet_pio_spi_obj_t *self, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits); + +extern bool common_hal_wiznet_pio_spi_try_lock(wiznet_pio_spi_obj_t *self); +extern bool common_hal_wiznet_pio_spi_has_lock(wiznet_pio_spi_obj_t *self); +extern void common_hal_wiznet_pio_spi_unlock(wiznet_pio_spi_obj_t *self); + +// Writes out the given data. +extern bool common_hal_wiznet_pio_spi_write(wiznet_pio_spi_obj_t *self, const uint8_t *data, size_t len); + +// Reads in len bytes while outputting the byte write_value. +extern bool common_hal_wiznet_pio_spi_read(wiznet_pio_spi_obj_t *self, uint8_t *data, size_t len, uint8_t write_value); + +// Reads and write len bytes simultaneously. +extern bool common_hal_wiznet_pio_spi_transfer(wiznet_pio_spi_obj_t *self, const uint8_t *data_out, uint8_t *data_in, size_t len); + +extern wiznet_pio_spi_obj_t *validate_obj_is_wiznet_pio_spi_bus(mp_obj_t obj_in, qstr arg_name); diff --git a/ports/raspberrypi/bindings/wiznet/__init__.c b/ports/raspberrypi/bindings/wiznet/__init__.c new file mode 100755 index 00000000000..6ed3c2886cc --- /dev/null +++ b/ports/raspberrypi/bindings/wiznet/__init__.c @@ -0,0 +1,29 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/microcontroller/Pin.h" +#include "bindings/wiznet/PIO_SPI.h" + +#include "py/runtime.h" + +static const mp_rom_map_elem_t wiznet_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_wiznet) }, + { MP_ROM_QSTR(MP_QSTR_PIO_SPI), MP_ROM_PTR(&wiznet_pio_spi_type) }, +}; + +static MP_DEFINE_CONST_DICT(wiznet_module_globals, wiznet_module_globals_table); + +const mp_obj_module_t wiznet_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&wiznet_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_wiznet, wiznet_module); diff --git a/ports/raspberrypi/bindings/wiznet/__init__.h b/ports/raspberrypi/bindings/wiznet/__init__.h new file mode 100755 index 00000000000..370e233985f --- /dev/null +++ b/ports/raspberrypi/bindings/wiznet/__init__.h @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft +// +// SPDX-License-Identifier: MIT + +#pragma once diff --git a/ports/raspberrypi/boards/adafruit_fruit_jam/mpconfigboard.mk b/ports/raspberrypi/boards/adafruit_fruit_jam/mpconfigboard.mk index 086c823adb4..8c46cc0713b 100644 --- a/ports/raspberrypi/boards/adafruit_fruit_jam/mpconfigboard.mk +++ b/ports/raspberrypi/boards/adafruit_fruit_jam/mpconfigboard.mk @@ -11,5 +11,8 @@ EXTERNAL_FLASH_DEVICES = "W25Q128JVxQ" CIRCUITPY_SDIOIO = 1 +CIRCUITPY_PICOGAME = 1 +CIRCUITPY_PICOGAME_FRAMEBUFFER = 1 + # CIRCUITPY_DISPLAY_FONT = $(TOP)/tools/fonts/unifont-16.0.02-all.bdf # CIRCUITPY_FONT_EXTRA_CHARACTERS = "🖮🖱️" diff --git a/ports/raspberrypi/boards/adafruit_itsybitsy_rp2040/mpconfigboard.mk b/ports/raspberrypi/boards/adafruit_itsybitsy_rp2040/mpconfigboard.mk index 965acff2775..781dfe46ad1 100644 --- a/ports/raspberrypi/boards/adafruit_itsybitsy_rp2040/mpconfigboard.mk +++ b/ports/raspberrypi/boards/adafruit_itsybitsy_rp2040/mpconfigboard.mk @@ -6,4 +6,4 @@ USB_MANUFACTURER = "Adafruit" CHIP_VARIANT = RP2040 CHIP_FAMILY = rp2 -EXTERNAL_FLASH_DEVICES = "W25Q64JVxQ" +EXTERNAL_FLASH_DEVICES = "W25Q64JVxQ,GD25Q64C" diff --git a/ports/raspberrypi/boards/adafruit_kb2040/mpconfigboard.mk b/ports/raspberrypi/boards/adafruit_kb2040/mpconfigboard.mk index f9e1e2cf157..08c85bb3f46 100644 --- a/ports/raspberrypi/boards/adafruit_kb2040/mpconfigboard.mk +++ b/ports/raspberrypi/boards/adafruit_kb2040/mpconfigboard.mk @@ -6,4 +6,4 @@ USB_MANUFACTURER = "Adafruit" CHIP_VARIANT = RP2040 CHIP_FAMILY = rp2 -EXTERNAL_FLASH_DEVICES = "W25Q64JVxQ" +EXTERNAL_FLASH_DEVICES = "W25Q64JVxQ,GD25Q64C" diff --git a/ports/raspberrypi/boards/adafruit_macropad_rp2040/mpconfigboard.mk b/ports/raspberrypi/boards/adafruit_macropad_rp2040/mpconfigboard.mk index 4d708bcf794..ea3d5a80fb9 100644 --- a/ports/raspberrypi/boards/adafruit_macropad_rp2040/mpconfigboard.mk +++ b/ports/raspberrypi/boards/adafruit_macropad_rp2040/mpconfigboard.mk @@ -6,4 +6,4 @@ USB_MANUFACTURER = "Adafruit" CHIP_VARIANT = RP2040 CHIP_FAMILY = rp2 -EXTERNAL_FLASH_DEVICES = "W25Q64JVxQ" +EXTERNAL_FLASH_DEVICES = "W25Q64JVxQ,GD25Q64C" diff --git a/ports/raspberrypi/boards/adafruit_qt2040_trinkey/mpconfigboard.mk b/ports/raspberrypi/boards/adafruit_qt2040_trinkey/mpconfigboard.mk index 5f07632b4f6..4252fafba2e 100644 --- a/ports/raspberrypi/boards/adafruit_qt2040_trinkey/mpconfigboard.mk +++ b/ports/raspberrypi/boards/adafruit_qt2040_trinkey/mpconfigboard.mk @@ -6,4 +6,4 @@ USB_MANUFACTURER = "Adafruit" CHIP_VARIANT = RP2040 CHIP_FAMILY = rp2 -EXTERNAL_FLASH_DEVICES = "W25Q64JVxQ" +EXTERNAL_FLASH_DEVICES = "W25Q64JVxQ,GD25Q64C" diff --git a/ports/raspberrypi/boards/adafruit_qtpy_rp2040/mpconfigboard.mk b/ports/raspberrypi/boards/adafruit_qtpy_rp2040/mpconfigboard.mk index 8258f878336..2b8431454b0 100644 --- a/ports/raspberrypi/boards/adafruit_qtpy_rp2040/mpconfigboard.mk +++ b/ports/raspberrypi/boards/adafruit_qtpy_rp2040/mpconfigboard.mk @@ -6,4 +6,4 @@ USB_MANUFACTURER = "Adafruit" CHIP_VARIANT = RP2040 CHIP_FAMILY = rp2 -EXTERNAL_FLASH_DEVICES = "W25Q64JVxQ" +EXTERNAL_FLASH_DEVICES = "W25Q64JVxQ,GD25Q64C" diff --git a/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/mpconfigboard.mk b/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/mpconfigboard.mk index 14f27c07b99..edb4f3bb3d8 100644 --- a/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/mpconfigboard.mk +++ b/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/mpconfigboard.mk @@ -15,6 +15,9 @@ EXTERNAL_FLASH_DEVICES = "GD25Q64C" CIRCUITPY__EVE = 1 +# The default is -O3. +OPTIMIZATION_FLAGS = -Os + FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_asyncio FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Bitmap_Font FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Shapes diff --git a/ports/raspberrypi/boards/pajenicko_picopad/mpconfigboard.mk b/ports/raspberrypi/boards/pajenicko_picopad/mpconfigboard.mk index 8a364e4b840..b5df0e6ecb5 100644 --- a/ports/raspberrypi/boards/pajenicko_picopad/mpconfigboard.mk +++ b/ports/raspberrypi/boards/pajenicko_picopad/mpconfigboard.mk @@ -13,10 +13,17 @@ CIRCUITPY_USB_HOST = 0 CIRCUITPY_KEYPAD = 1 CIRCUITPY_STAGE = 1 +CIRCUITPY_PICOGAME = 1 +CIRCUITPY_PICOGAME_FAST_DISPLAY = 1 +CIRCUITPY_PICOGAME_RGB444 = 1 CIRCUITPY_AUDIOIO = 1 CIRCUITPY_AUDIOEFFECTS = 0 -CIRCUITPY__EVE = 1 +# Peripherals this board physically lacks: no FT8xx EVE display, no camera for the +# qrio QR *decoder* (QR generation = pure-Python adafruit_miniqr, unaffected), and no +# DVI/HDMI connector. Dropping them frees flash for the picogame engine. +CIRCUITPY__EVE = 0 +CIRCUITPY_QRIO = 0 CIRCUITPY_CYW43 = 1 CIRCUITPY_SSL = 1 @@ -26,7 +33,6 @@ CIRCUITPY_MDNS = 1 CIRCUITPY_SOCKETPOOL = 1 CIRCUITPY_WIFI = 1 -CIRCUITPY_PICODVI = 1 # Pimoroni PicoSystem peripherals are compatible, we can use of existing ugame.py FROZEN_MPY_DIRS += $(TOP)/frozen/circuitpython-stage/picosystem @@ -44,3 +50,8 @@ CFLAGS += \ # Must be accompanied by a linker script change CFLAGS += -DCIRCUITPY_FIRMWARE_SIZE='(1536 * 1024)' + +# The rp2 port default is -O3; on this Cortex-M0+ (no SIMD/FPU, 16 KB XIP cache) -O2 plus +# these five loop passes measures within +-1% of -O3 across the picogame render kernels +# while using ~150 KB less flash (gc.o/vm.o stay -O3 via SUPEROPT regardless). +OPTIMIZATION_FLAGS = -O2 -funswitch-loops -fpredictive-commoning -fgcse-after-reload -ftree-partial-pre -fsplit-paths diff --git a/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/mpconfigboard.mk b/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/mpconfigboard.mk index 69b23a72be7..0a56b55a0c1 100644 --- a/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/mpconfigboard.mk +++ b/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/mpconfigboard.mk @@ -36,4 +36,3 @@ CFLAGS += -DCIRCUITPY_FIRMWARE_SIZE='(1536 * 1024)' FROZEN_MPY_DIRS += $(TOP)/frozen/circuitpython-pcf85063a FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register -FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SD diff --git a/ports/raspberrypi/boards/pimoroni_pico_dv_base_w/mpconfigboard.mk b/ports/raspberrypi/boards/pimoroni_pico_dv_base_w/mpconfigboard.mk index 016dd0d7269..02cbe3b69b7 100644 --- a/ports/raspberrypi/boards/pimoroni_pico_dv_base_w/mpconfigboard.mk +++ b/ports/raspberrypi/boards/pimoroni_pico_dv_base_w/mpconfigboard.mk @@ -19,6 +19,7 @@ CIRCUITPY_SOCKETPOOL = 1 CIRCUITPY_WIFI = 1 CIRCUITPY_PICODVI = 1 +CIRCUITPY_AUDIOEFFECTS = 0 CIRCUITPY_AUDIOFILEWRITER = 0 CIRCUITPY_BLEIO_HCI = 0 diff --git a/ports/raspberrypi/boards/wiznet_w55rp20_evb_pico/board.c b/ports/raspberrypi/boards/wiznet_w55rp20_evb_pico/board.c new file mode 100755 index 00000000000..e6a868ab212 --- /dev/null +++ b/ports/raspberrypi/boards/wiznet_w55rp20_evb_pico/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/wiznet_w55rp20_evb_pico/mpconfigboard.h b/ports/raspberrypi/boards/wiznet_w55rp20_evb_pico/mpconfigboard.h new file mode 100755 index 00000000000..b5ccfb5ffd9 --- /dev/null +++ b/ports/raspberrypi/boards/wiznet_w55rp20_evb_pico/mpconfigboard.h @@ -0,0 +1,30 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#define MICROPY_HW_BOARD_NAME "W55RP20-EVB-Pico" +#define MICROPY_HW_MCU_NAME "rp2040" + +#define MICROPY_HW_LED_STATUS (&pin_GPIO19) + +#define DEFAULT_SPI_BUS_SCK (&pin_GPIO21) +#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO23) +#define DEFAULT_SPI_BUS_MISO (&pin_GPIO22) + +#define DEFAULT_UART_BUS_RX (&pin_GPIO1) +#define DEFAULT_UART_BUS_TX (&pin_GPIO0) + +// Wiznet HW config. +#define MICROPY_HW_WIZNET_PIO_SPI_ID (0) +#define MICROPY_HW_WIZNET_PIO_SPI_BAUDRATE (20 * 1000 * 1000) +#define MICROPY_HW_WIZNET_PIO_SPI_SCK (21) +#define MICROPY_HW_WIZNET_PIO_SPI_MOSI (23) +#define MICROPY_HW_WIZNET_PIO_SPI_MISO (22) +#define MICROPY_HW_WIZNET_PIO_PIN_CS (20) +#define MICROPY_HW_WIZNET_PIO_PIN_RST (25) +// Connecting the INTN pin enables RECV interrupt handling of incoming data. +#define MICROPY_HW_WIZNET_PIN_INTN (24) diff --git a/ports/raspberrypi/boards/wiznet_w55rp20_evb_pico/mpconfigboard.mk b/ports/raspberrypi/boards/wiznet_w55rp20_evb_pico/mpconfigboard.mk new file mode 100755 index 00000000000..d43195c0f2a --- /dev/null +++ b/ports/raspberrypi/boards/wiznet_w55rp20_evb_pico/mpconfigboard.mk @@ -0,0 +1,16 @@ +USB_VID = 0x2E8A +USB_PID = 0x0005 +USB_PRODUCT = "W55RP20-EVB-Pico" +USB_MANUFACTURER = "WIZnet" + +CHIP_VARIANT = RP2040 +CHIP_FAMILY = rp2 + +EXTERNAL_FLASH_DEVICES = "PY25Q16HB" + +CIRCUITPY_WIZNET = 1 +CIRCUITPY__EVE = 1 +CIRCUITPY_SSL = 1 +CIRCUITPY_USB_HOST = 0 + +OPTIMIZATION_FLAGS = -O2 diff --git a/ports/raspberrypi/boards/wiznet_w55rp20_evb_pico/pico-sdk-configboard.h b/ports/raspberrypi/boards/wiznet_w55rp20_evb_pico/pico-sdk-configboard.h new file mode 100755 index 00000000000..110195b7794 --- /dev/null +++ b/ports/raspberrypi/boards/wiznet_w55rp20_evb_pico/pico-sdk-configboard.h @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Put board-specific pico-sdk definitions here. This file must exist. diff --git a/ports/raspberrypi/boards/wiznet_w55rp20_evb_pico/pins.c b/ports/raspberrypi/boards/wiznet_w55rp20_evb_pico/pins.c new file mode 100755 index 00000000000..37ecfd3dfb1 --- /dev/null +++ b/ports/raspberrypi/boards/wiznet_w55rp20_evb_pico/pins.c @@ -0,0 +1,72 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_GP0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_GP1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_GP2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_GP5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_GP6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_GP8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_GP9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_GP10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_GP11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_GP12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_GP13), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_GP14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_GP15), MP_ROM_PTR(&pin_GPIO15) }, + + { MP_ROM_QSTR(MP_QSTR_VBUS_SENSE), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_GP18), MP_ROM_PTR(&pin_GPIO18) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_GP19), MP_ROM_PTR(&pin_GPIO19) }, + + { MP_ROM_QSTR(MP_QSTR_W5K_CS), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_GP20), MP_ROM_PTR(&pin_GPIO20) }, + + { MP_ROM_QSTR(MP_QSTR_W5K_SCK), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_GP21), MP_ROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_W5K_MISO), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_GP22), MP_ROM_PTR(&pin_GPIO22) }, + + { MP_ROM_QSTR(MP_QSTR_W5K_MOSI), MP_ROM_PTR(&pin_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_GP23), MP_ROM_PTR(&pin_GPIO23) }, + + { MP_ROM_QSTR(MP_QSTR_W5K_INT), MP_ROM_PTR(&pin_GPIO24) }, + { MP_ROM_QSTR(MP_QSTR_GP24), MP_ROM_PTR(&pin_GPIO24) }, + + { MP_ROM_QSTR(MP_QSTR_W5K_RST), MP_ROM_PTR(&pin_GPIO25) }, + { MP_ROM_QSTR(MP_QSTR_GP25), MP_ROM_PTR(&pin_GPIO25) }, + + { MP_ROM_QSTR(MP_QSTR_GP26_A0), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_GP26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO26) }, + + { MP_ROM_QSTR(MP_QSTR_GP27_A1), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_GP27), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO27) }, + + { MP_ROM_QSTR(MP_QSTR_GP28_A2), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_GP28), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO28) }, + + { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_VOLTAGE_MONITOR), MP_ROM_PTR(&pin_GPIO29) }, + + { MP_ROM_QSTR(MP_QSTR_W5K_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, + { MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_i2c_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/raspberrypi/common-hal/audioi2sin/I2SIn.c b/ports/raspberrypi/common-hal/audioi2sin/I2SIn.c index 7bcfcb88ac7..bb0b0204111 100644 --- a/ports/raspberrypi/common-hal/audioi2sin/I2SIn.c +++ b/ports/raspberrypi/common-hal/audioi2sin/I2SIn.c @@ -212,11 +212,10 @@ static const uint16_t i2sin_program_left_justified_swap_32[] = { // a 32-BCLK frame is one push (right<<16 | left), at 24/32 a 64-BCLK frame is // two pushes (right then left). // -// The resync's own `wait 0/1 gpio B` already lands on the first data bit, so -// against a CircuitPython clock source this program recovers the transmitted word -// bit-exactly with no instruction for the Philips delay bit. The -// `left_justified` variant is that program plus one more BCLK of skew, -// the other of the two possible alignments; +// The WS resync lands on the first data bit, so the program above is +// the `left_justified` alignment: data starts on the WS edge and no instruction +// is spent on a delay bit. The default (Philips) alignment is that program plus +// one more BCLK of skew, the other of the two possible alignments. // // Free-running after the initial sync: the external frame must be exactly // 2 x bits_per_channel BCLKs, the same assumption internal clock mode already bakes @@ -226,16 +225,13 @@ static const uint16_t i2sin_program_left_justified_swap_32[] = { #define I2SIN_EXT_CLOCK_WRAP_TARGET(len) ((int)(len) - 5) static size_t build_i2sin_ext_clock_program(uint16_t *prog, uint8_t bclk, uint8_t ws, - bool left_justified, bool invert_bit_clock) { - // Sampling on the falling edge of BCLK is the same program with the - // polarity of every BCLK wait flipped. - const uint16_t invert = invert_bit_clock ? 0x0080 : 0x0000; - const uint16_t wait_0_bclk = (0x2000 | bclk) ^ invert; - const uint16_t wait_1_bclk = (0x2080 | bclk) ^ invert; + bool left_justified) { + const uint16_t wait_0_bclk = 0x2000 | bclk; + const uint16_t wait_1_bclk = 0x2080 | bclk; size_t len = 0; prog[len++] = 0x2000 | ws; // wait 0 gpio W prog[len++] = 0x2080 | ws; // wait 1 gpio W - if (left_justified) { + if (!left_justified) { prog[len++] = wait_1_bclk; // one more BCLK of skew } const size_t bitloop = len + 1; @@ -262,7 +258,7 @@ void common_hal_audioi2sin_i2sin_construct(audioi2sin_i2sin_obj_t *self, const mcu_pin_obj_t *data, const mcu_pin_obj_t *main_clock, uint32_t sample_rate, uint8_t bit_depth, uint8_t output_bit_depth, bool mono, bool left_justified, bool samples_signed, - bool external_clock, bool invert_bit_clock) { + bool external_clock) { if (main_clock != NULL) { mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_main_clock); @@ -295,7 +291,7 @@ void common_hal_audioi2sin_i2sin_construct(audioi2sin_i2sin_obj_t *self, program_len = build_i2sin_ext_clock_program(ext_clock_program, i2s_wait_gpio_index(bit_clock, gpio_offset), i2s_wait_gpio_index(word_select, gpio_offset), - left_justified, invert_bit_clock); + left_justified); program = ext_clock_program; wait_gpio_mask = PIO_PINMASK_OR(PIO_PINMASK_FROM_PIN(bit_clock->number), PIO_PINMASK_FROM_PIN(word_select->number)); diff --git a/ports/raspberrypi/common-hal/mdns/RemoteService.c b/ports/raspberrypi/common-hal/mdns/RemoteService.c index 650e86da9d9..3d80199397e 100644 --- a/ports/raspberrypi/common-hal/mdns/RemoteService.c +++ b/ports/raspberrypi/common-hal/mdns/RemoteService.c @@ -39,6 +39,3 @@ mp_obj_t common_hal_mdns_remoteservice_get_ipv4_address(mdns_remoteservice_obj_t } return common_hal_ipaddress_new_ipv4address(addr); } - -void common_hal_mdns_remoteservice_deinit(mdns_remoteservice_obj_t *self) { -} diff --git a/ports/raspberrypi/common-hal/mdns/Server.c b/ports/raspberrypi/common-hal/mdns/Server.c index ac0c73389b1..1afccf4eb48 100644 --- a/ports/raspberrypi/common-hal/mdns/Server.c +++ b/ports/raspberrypi/common-hal/mdns/Server.c @@ -42,10 +42,15 @@ void mdns_server_construct(mdns_server_obj_t *self, bool workflow) { } self->inited = true; + self->instance_name[0] = '\0'; + self->num_txt_records = 0; + self->txt_storage = NULL; + uint8_t mac[6]; wifi_radio_get_mac_address(&common_hal_wifi_radio_obj, mac); - snprintf(self->default_hostname, sizeof(self->default_hostname), "cpy-%02x%02x%02x", mac[3], mac[4], mac[5]); - common_hal_mdns_server_set_hostname(self, self->default_hostname); + char default_hostname[sizeof("cpy-XXXXXX")]; + snprintf(default_hostname, sizeof(default_hostname), "cpy-%02x%02x%02x", mac[3], mac[4], mac[5]); + common_hal_mdns_server_set_hostname(self, default_hostname); if (workflow) { // Add a second host entry to respond to "circuitpython.local" queries as well. @@ -91,15 +96,18 @@ void common_hal_mdns_server_set_hostname(mdns_server_obj_t *self, const char *ho mdns_resp_add_netif(NETIF_STA, hostname); } - self->hostname = hostname; + strlcpy(self->hostname, hostname, sizeof(self->hostname)); } const char *common_hal_mdns_server_get_instance_name(mdns_server_obj_t *self) { + if (self->instance_name[0] == '\0') { + return self->hostname; + } return self->instance_name; } void common_hal_mdns_server_set_instance_name(mdns_server_obj_t *self, const char *instance_name) { - self->instance_name = instance_name; + strlcpy(self->instance_name, instance_name, sizeof(self->instance_name)); } typedef struct { @@ -288,15 +296,64 @@ static void srv_txt_cb(struct mdns_service *service, void *ptr) { } } +// Take our own copies of the TXT records. lwip only stores srv_txt_cb and this +// object, and calls back at packet-build time, so the caller's strings must +// outlive the call -- and the caller hands us pointers into GC-heap strings. +// +// WARNING: the copies live on the GC heap, which is only safe because TXT +// records can reach us solely through the Python binding, so the VM is +// necessarily running and this object is itself a GC object that dies with the +// same heap. The supervisor's static mdns_server_obj_t never gets TXT records +// (web_workflow.c passes NULL, 0). If supervisor code ever needs to advertise +// TXT records, this must move off the GC heap first -- an inline pool in +// mdns_server_obj_t, or port_malloc -- or the records will dangle after the +// first VM reset. static void assign_txt_records(mdns_server_obj_t *self, const char *txt_records[], size_t num_txt_records) { - size_t allowed_num_txt_records = MDNS_MAX_TXT_RECORDS < num_txt_records ? MDNS_MAX_TXT_RECORDS : num_txt_records; - self->num_txt_records = allowed_num_txt_records; - for (size_t i = 0; i < allowed_num_txt_records; i++) { - self->txt_records[i] = txt_records[i]; + size_t total = 0; + for (size_t i = 0; i < num_txt_records; i++) { + total += strlen(txt_records[i]) + 1; } + + // Build the replacement before touching self, so that the allocation, and + // any MemoryError it raises, happens outside the lwip lock below. + char *storage = NULL; + const char *records[MDNS_MAX_TXT_RECORDS]; + if (total > 0) { + storage = m_malloc_maybe(total); + if (storage == NULL) { + m_malloc_fail(total); + } + char *next = storage; + for (size_t i = 0; i < num_txt_records; i++) { + size_t size = strlen(txt_records[i]) + 1; + memcpy(next, txt_records[i], size); + records[i] = next; + next += size; + } + } + + // srv_txt_cb reads these from the lwip IRQ, so hold lwip off while they + // change. Otherwise a callback already part way through the old records + // keeps pointers into storage we are about to release. + MICROPY_PY_LWIP_ENTER + char *old_storage = self->txt_storage; + for (size_t i = 0; i < num_txt_records; i++) { + self->txt_records[i] = records[i]; + } + self->txt_storage = storage; + self->num_txt_records = num_txt_records; + MICROPY_PY_LWIP_EXIT + + // Nothing can be holding pointers into it now, so release it here rather + // than leaving the GC to do it at an unpredictable time. + m_free(old_storage); } void common_hal_mdns_server_advertise_service(mdns_server_obj_t *self, const char *service_type, const char *protocol, mp_int_t port, const char *txt_records[], size_t num_txt_records) { + // Check before touching any state, so a rejected call leaves the existing + // advertisement alone. + mp_arg_validate_length_max(num_txt_records, MDNS_MAX_TXT_RECORDS, MP_QSTR_txt_records); + enum mdns_sd_proto proto = DNSSD_PROTO_UDP; if (strcmp(protocol, "_tcp") == 0) { proto = DNSSD_PROTO_TCP; @@ -316,7 +373,7 @@ void common_hal_mdns_server_advertise_service(mdns_server_obj_t *self, const cha } assign_txt_records(self, txt_records, num_txt_records); - int8_t slot = mdns_resp_add_service(NETIF_STA, self->instance_name, service_type, proto, port, srv_txt_cb, self); + int8_t slot = mdns_resp_add_service(NETIF_STA, common_hal_mdns_server_get_instance_name(self), service_type, proto, port, srv_txt_cb, self); if (slot < 0) { mp_raise_RuntimeError(MP_ERROR_TEXT("Out of MDNS service slots")); return; diff --git a/ports/raspberrypi/common-hal/mdns/Server.h b/ports/raspberrypi/common-hal/mdns/Server.h index 620e201c897..041add08818 100644 --- a/ports/raspberrypi/common-hal/mdns/Server.h +++ b/ports/raspberrypi/common-hal/mdns/Server.h @@ -14,11 +14,16 @@ typedef struct { mp_obj_base_t base; - const char *hostname; - const char *instance_name; - char default_hostname[sizeof("cpy-XXXXXX")]; + // Store copies rather than the caller's pointers. The setters are handed + // GC-heap strings, which are recycled when the VM resets while this object + // (and the web workflow's static one) lives on. + char hostname[64]; // RFC 6762 Appendix A - 63 bytes for label + 1 for NUL + char instance_name[64]; // RFC 6763 Section 7.2 - 63 bytes + 1 for NUL const char *service_type[MDNS_MAX_SERVICES]; size_t num_txt_records; + // Owned copies of the TXT records, packed NUL-separated into txt_storage, + // which lives on the GC heap. See the warning on assign_txt_records(). + char *txt_storage; const char *txt_records[MDNS_MAX_TXT_RECORDS]; // Track if this object owns access to the underlying MDNS service. bool inited; diff --git a/ports/raspberrypi/common-hal/microcontroller/Processor.c b/ports/raspberrypi/common-hal/microcontroller/Processor.c index c093a964c39..fbbef51acb4 100644 --- a/ports/raspberrypi/common-hal/microcontroller/Processor.c +++ b/ports/raspberrypi/common-hal/microcontroller/Processor.c @@ -10,6 +10,7 @@ #include "py/mphal.h" #include "py/runtime.h" #include "common-hal/microcontroller/Processor.h" +#include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/microcontroller/Processor.h" #include "shared-bindings/microcontroller/ResetReason.h" #include "shared-bindings/time/__init__.h" @@ -20,6 +21,11 @@ #include "hardware/vreg.h" #include "hardware/watchdog.h" +#ifdef CIRCUITPY_PSRAM_CHIP_SELECT +#include "hardware/regs/qmi.h" +#include "hardware/structs/qmi.h" +#endif + #if PICO_RP2040 #include "hardware/regs/vreg_and_chip_reset.h" #include "hardware/structs/vreg_and_chip_reset.h" @@ -50,6 +56,26 @@ uint32_t common_hal_mcu_processor_get_frequency(void) { return clock_get_hz(clk_sys); } +#ifdef CIRCUITPY_PSRAM_CHIP_SELECT +void __no_inline_not_in_flash_func(mcu_processor_update_psram_timing)(uint32_t sys_clk_khz) { + // MAX_SELECT is in units of 64 system clock cycles; PSRAM allows 8 us max CS + // assertion. Use 7.5 us so there is margin at any clk_sys. + uint32_t max_select = (75 * sys_clk_khz) / 640000; + // MIN_DESELECT is in system clock cycles; PSRAM needs 50 ns min CS + // deassertion. Round up so we are never under. + uint32_t min_deselect = (sys_clk_khz + 19999) / 20000; + + qmi_hw->m[1].timing = + QMI_M0_TIMING_PAGEBREAK_VALUE_1024 << QMI_M0_TIMING_PAGEBREAK_LSB | // Break between pages. + 3 << QMI_M0_TIMING_SELECT_HOLD_LSB | // Delay releasing CS for 3 extra system cycles. + 1 << QMI_M0_TIMING_COOLDOWN_LSB | + 1 << QMI_M0_TIMING_RXDELAY_LSB | + max_select << QMI_M0_TIMING_MAX_SELECT_LSB | + min_deselect << QMI_M0_TIMING_MIN_DESELECT_LSB | + 2 << QMI_M0_TIMING_CLKDIV_LSB; +} +#endif + void common_hal_mcu_processor_set_frequency(mcu_processor_obj_t *self, uint32_t frequency) { uint vco, postdiv1, postdiv2; uint32_t freq_khz = frequency / 1000; @@ -68,7 +94,17 @@ void common_hal_mcu_processor_set_frequency(mcu_processor_obj_t *self, uint32_t vreg_set_voltage(voltage); // Wait for a stable voltage common_hal_time_delay_ms(10); + + #ifdef CIRCUITPY_PSRAM_CHIP_SELECT + // Prevent interrupt handlers from accessing PSRAM until its timing matches + // the new system clock. + common_hal_mcu_disable_interrupts(); + #endif set_sys_clock_khz(freq_khz, false); + #ifdef CIRCUITPY_PSRAM_CHIP_SELECT + mcu_processor_update_psram_timing(freq_khz); + common_hal_mcu_enable_interrupts(); + #endif } void common_hal_mcu_processor_get_uid(uint8_t raw_id[]) { diff --git a/ports/raspberrypi/common-hal/microcontroller/Processor.h b/ports/raspberrypi/common-hal/microcontroller/Processor.h index df1e1cf2333..9503eaf21f4 100644 --- a/ports/raspberrypi/common-hal/microcontroller/Processor.h +++ b/ports/raspberrypi/common-hal/microcontroller/Processor.h @@ -16,3 +16,7 @@ typedef struct { mp_obj_base_t base; // Stores no state currently. } mcu_processor_obj_t; + +#ifdef CIRCUITPY_PSRAM_CHIP_SELECT +void mcu_processor_update_psram_timing(uint32_t sys_clk_khz); +#endif diff --git a/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c b/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c index 8dddb451d96..53ed37cefbe 100644 --- a/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c +++ b/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -364,6 +364,11 @@ bool common_hal_picodvi_framebuffer_deinited(picodvi_framebuffer_obj_t *self) { void common_hal_picodvi_framebuffer_refresh(picodvi_framebuffer_obj_t *self) { } +void common_hal_picodvi_framebuffer_wait_for_vblank(picodvi_framebuffer_obj_t *self) { + // No frame-boundary counter on the RP2040 PIO-DVI path; vblank sync is a no-op here (the + // tear-free full-repaint path is RP2350/HSTX, e.g. Fruit Jam). Present so the binding links. +} + int common_hal_picodvi_framebuffer_get_width(picodvi_framebuffer_obj_t *self) { return self->width; } diff --git a/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c b/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c index 2dc19558314..8c846ffc440 100644 --- a/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c +++ b/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c @@ -29,6 +29,7 @@ #include "py/gc.h" #include "py/runtime.h" #include "shared-bindings/time/__init__.h" +#include "supervisor/background_callback.h" #include "supervisor/port.h" #include "pico/stdlib.h" @@ -160,6 +161,11 @@ static uint32_t vactive_line720[VACTIVE_LEN] = { picodvi_framebuffer_obj_t *active_picodvi = NULL; +// Incremented once per completed frame by the DMA frame-boundary IRQ, so refresh() can block +// until the next frame starts (vblank sync). A full-framebuffer repaint that begins right after a +// frame boundary then races ahead of scanout, avoiding tearing without a second framebuffer. +static volatile uint32_t framebuffer_frame_count = 0; + static void __not_in_flash_func(dma_irq_handler)(void) { if (active_picodvi == NULL) { return; @@ -171,6 +177,7 @@ static void __not_in_flash_func(dma_irq_handler)(void) { // will trigger the pixel channel). dma_channel_hw_t *ch = &dma_hw->ch[active_picodvi->dma_command_channel]; ch->al3_read_addr_trig = (uintptr_t)active_picodvi->dma_commands; + framebuffer_frame_count++; // frame boundary: scanout has wrapped back to the top } bool common_hal_picodvi_framebuffer_preflight( @@ -625,6 +632,23 @@ bool common_hal_picodvi_framebuffer_deinited(picodvi_framebuffer_obj_t *self) { void common_hal_picodvi_framebuffer_refresh(picodvi_framebuffer_obj_t *self) { } +void common_hal_picodvi_framebuffer_wait_for_vblank(picodvi_framebuffer_obj_t *self) { + // Block until the next frame boundary (vblank). A full-framebuffer repaint issued right after + // this returns starts at the top of scanout and stays ahead of the beam, so it does not tear + // mid-screen. Bounded by a timeout so a stopped or absent DVI signal can never hang the caller. + if (self != active_picodvi) { + return; + } + uint32_t start = framebuffer_frame_count; + uint64_t timeout = common_hal_time_monotonic_ms() + 30; // ~2 frames @ 60 Hz + while (framebuffer_frame_count == start) { + RUN_BACKGROUND_TASKS; // don't starve USB host / other tasks during the ~<16 ms wait + if (common_hal_time_monotonic_ms() >= timeout) { + break; + } + } +} + int common_hal_picodvi_framebuffer_get_width(picodvi_framebuffer_obj_t *self) { return self->width; } diff --git a/ports/raspberrypi/common-hal/picogame/Display.c b/ports/raspberrypi/common-hal/picogame/Display.c new file mode 100644 index 00000000000..9c0fcd17011 --- /dev/null +++ b/ports/raspberrypi/common-hal/picogame/Display.c @@ -0,0 +1,165 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#include "common-hal/picogame/Display.h" + +#include "py/runtime.h" +#include "shared-module/picogame/__init__.h" +#include "shared-module/displayio/display_core.h" +#include "shared-bindings/displayio/__init__.h" +#include "shared-bindings/fourwire/FourWire.h" + +#include "hardware/spi.h" +#include "hardware/dma.h" + +// Claimed once and REUSED across every Display construct (incl. across soft resets / each game +// launched via supervisor.set_next_code_file). We claim via the raw pico-sdk, which CircuitPython +// does NOT release on a soft reset -- so claiming per-construct leaked a channel every game until +// "*** PANIC *** No DMA channels available" (and starved board.DISPLAY's own DMA, halving FPS). +// A C static survives a soft reset, so we remember and reuse our one channel; a hard reset frees it. +static int s_picogame_dma_chan = -1; + +void common_hal_picogame_display_construct(picogame_display_obj_t *self, + busdisplay_busdisplay_obj_t *display, bool rgb444) { + self->display = display; + #if CIRCUITPY_PICOGAME_RGB444 + self->rgb444 = rgb444; + #else + if (rgb444) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("Operation or feature not supported")); + } + self->rgb444 = false; + #endif + + // The fast path needs raw SPI access; only FourWire SPI buses are supported. + if (!mp_obj_is_type(display->bus.bus, &fourwire_fourwire_type)) { + mp_raise_TypeError_varg(MP_ERROR_TEXT("%q must be of type %q, not %q"), + MP_QSTR_display, MP_QSTR_FourWire, mp_obj_get_type(display->bus.bus)->name); + } + fourwire_fourwire_obj_t *fw = MP_OBJ_TO_PTR(display->bus.bus); + self->spi = fw->bus->peripheral; + + #if CIRCUITPY_PICOGAME_RGB444 + // Tell the panel which pixel format we'll send (COLMOD). Asserting it here also recovers from + // a previous program that left the panel in the other format (the setting survives soft reset). + picogame_set_pixel_format(display, rgb444); + #endif + + if (s_picogame_dma_chan < 0) { + s_picogame_dma_chan = dma_claim_unused_channel(true); + } + self->dma_chan = s_picogame_dma_chan; + + // Configure the channel ONCE (dreq, 8-bit, read-incr, write addr = SPI data reg). Per-strip we + // then only set the read address + transfer count and trigger -- no per-strip reconfiguration. + dma_channel_config c = dma_channel_get_default_config(self->dma_chan); + channel_config_set_transfer_data_size(&c, DMA_SIZE_8); + channel_config_set_dreq(&c, spi_get_dreq((spi_inst_t *)self->spi, true)); + channel_config_set_read_increment(&c, true); + channel_config_set_write_increment(&c, false); + dma_channel_configure(self->dma_chan, &c, + &spi_get_hw((spi_inst_t *)self->spi)->dr, NULL, 0, false); +} + +// Kick off an asynchronous TX-only DMA of `nbytes` from `buf` to the SPI FIFO (channel already +// configured in construct; just point it at `buf`, set the count, and trigger). +static void dma_start(int chan, const uint16_t *buf, size_t nbytes) { + dma_channel_set_read_addr(chan, buf, false); + dma_channel_set_trans_count(chan, nbytes, true); // true = trigger +} + +void common_hal_picogame_display_render(picogame_display_obj_t *self, + mp_obj_t *items, uint8_t *kinds, size_t n, + uint16_t *buf_a, uint16_t *buf_b, size_t buf_pixels, + int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t background, + int ox, int oy) { + + busdisplay_busdisplay_obj_t *display = self->display; + spi_inst_t *spi = (spi_inst_t *)self->spi; + + // RGB444 packs 2 px into 3 bytes, so each strip row must be an even number of pixels + // (whole bytes). Widen the region to even bounds; the extra <=1 px per side just repaints. + #if CIRCUITPY_PICOGAME_RGB444 + if (self->rgb444) { + x0 &= ~1; + x1 = (x1 + 1) & ~1; + if (x1 > display->core.width) { + x1 = display->core.width; + } + } + #endif + + // Compute geometry + open the GRAM window (set region, begin transaction, + // RAMWR). DC stays high for data after the first DATA send below, so the + // raw-DMA strips that follow need no DC toggling. + int region_w, strip_h; + int cx0 = x0, cy0 = y0, cx1 = x1, cy1 = y1; // strip_begin clamps these to the panel in place + if (!picogame_strip_begin(display, &cx0, &cy0, &cx1, &cy1, buf_pixels, ®ion_w, &strip_h)) { + return; + } + + uint16_t *bufs[2] = { buf_a, buf_b }; + int cur = 0; + bool first = true; + bool dma_inflight = false; + #if CIRCUITPY_PICOGAME_RGB444 + const bool rgb444 = self->rgb444; // hoist: invariant across all strips + #endif + + // A StripDraw callback may latch a BaseException (Ctrl-C / auto-reload). Like the portable + // picogame_render_region, we must re-raise it -- but only AFTER the in-flight DMA finishes and + // the bus transaction closes, so hold it here and propagate below (see the end of the function). + mp_obj_t pending = MP_OBJ_NULL; + + for (int sy = cy0; sy < cy1; sy += strip_h) { + int sh = picogame_imin(strip_h, cy1 - sy); + uint16_t *buf = bufs[cur]; + + // Blit this strip. When a DMA is in flight it transfers the *other* + // buffer, so this CPU work overlaps the SPI transfer. + pending = picogame_blit_strip_layers(buf, region_w, sy, sh, cx0, items, kinds, n, background, ox, oy); + + // RGB444: pack the just-blitted RGB565 strip in place (2 px -> 3 bytes) before sending. + // The pack overlaps the previous strip's DMA (we're transfer-bound), so it's ~free. + #if CIRCUITPY_PICOGAME_RGB444 + size_t nbytes = rgb444 + ? picogame_pack_rgb444(buf, (size_t)region_w * sh) + : (size_t)region_w * sh * 2; + #else + size_t nbytes = (size_t)region_w * sh * 2; + #endif + + if (first) { + // First strip goes through busdisplay: it drives DC high for data, + // which then stays high for the subsequent raw-DMA strips. + display->bus.send(display->bus.bus, DISPLAY_DATA, + CHIP_SELECT_UNTOUCHED, (uint8_t *)buf, nbytes); + first = false; + } else { + if (dma_inflight) { + dma_channel_wait_for_finish_blocking(self->dma_chan); + } + dma_start(self->dma_chan, buf, nbytes); + dma_inflight = true; + } + cur ^= 1; + if (pending != MP_OBJ_NULL) { // callback interrupted: this strip is queued, now stop + flush + break; + } + } + + if (dma_inflight) { + dma_channel_wait_for_finish_blocking(self->dma_chan); + } + while (spi_is_busy(spi)) { + // wait for the last bytes to leave the shift register before releasing CS + } + displayio_display_bus_end_transaction(&display->bus); + + if (pending != MP_OBJ_NULL) { // bus now closed -> safe to re-raise (Ctrl-C / reload) + nlr_raise(MP_OBJ_TO_PTR(pending)); + } +} diff --git a/ports/raspberrypi/common-hal/picogame/Display.h b/ports/raspberrypi/common-hal/picogame/Display.h new file mode 100644 index 00000000000..aaca229260f --- /dev/null +++ b/ports/raspberrypi/common-hal/picogame/Display.h @@ -0,0 +1,33 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT +// +// Fast display backend (RP2040): wraps an existing busdisplay and pushes pixels +// with asynchronous, double-buffered DMA so the CPU can blit the next strip +// while the current strip transfers over SPI. Reuses the busdisplay's SPI +// peripheral, window opcodes and dimensions - controller/resolution agnostic. + +#pragma once + +#include "py/obj.h" +#include "shared-bindings/busdisplay/BusDisplay.h" +#include "shared-module/picogame/Sprite.h" + +typedef struct { + mp_obj_base_t base; + busdisplay_busdisplay_obj_t *display; + void *spi; // spi_inst_t* (kept opaque to avoid pico-sdk in this header) + int dma_chan; + bool rgb444; // pack strips to 12-bit RGB444 before sending (~25% less SPI traffic) +} picogame_display_obj_t; + +void common_hal_picogame_display_construct(picogame_display_obj_t *self, + busdisplay_busdisplay_obj_t *display, bool rgb444); + +void common_hal_picogame_display_render(picogame_display_obj_t *self, + mp_obj_t *items, uint8_t *kinds, size_t n, + uint16_t *buf_a, uint16_t *buf_b, size_t buf_pixels, + int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t background, + int ox, int oy); diff --git a/ports/raspberrypi/common-hal/picogame/interp.c b/ports/raspberrypi/common-hal/picogame/interp.c new file mode 100644 index 00000000000..78cc8581cbb --- /dev/null +++ b/ports/raspberrypi/common-hal/picogame/interp.c @@ -0,0 +1,54 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +// Mode-7 inner row via the SIO INTERPOLATOR (RP2040 + RP2350). The interpolator does the +// whole per-pixel texture walk in hardware: lane0 = (fx >> shx) & (tw-1), lane1 = +// ((fy >> shy) & (th-1)) << log2(tw), POP_FULL = texture_base + lane0 + lane1 = the texel +// ADDRESS, and (ADD_RAW) both accumulators advance by their full-precision steps on the +// same pop. That replaces ~8 shift/mask/mul/add ops per pixel with one SIO read. +// +// Per-CORE hardware: each core has its own interp0, so this stays safe even if rows are +// ever split across cores (each configures its own). The engine claims no SDK +// lane locks: CircuitPython core does not use the interpolators, and the config is +// rewritten per row call anyway. +// +// Fast path constraints (the caller guards): PAL8 texture, no transparency, stride == tw, +// log2(tw)+log2(th) <= 16 (mode7 textures are 64..256 pow2 - always true there). + +#include "py/mpconfig.h" + +#if CIRCUITPY_PICOGAME + +#include +#include "hardware/interp.h" +#include "shared-module/picogame/__init__.h" // the prototype (PICOGAME_HAS_INTERP) + +void picogame_mode7_row_interp(uint16_t *dst, int n, + const uint8_t *tex, const uint16_t *pal, + uint32_t fx, uint32_t fy, int32_t stepx, int32_t stepy, + int shx, int shy, int ltw, int lth) { + interp_config c0 = interp_default_config(); + interp_config_set_shift(&c0, (uint)shx); + interp_config_set_mask(&c0, 0, (uint)(ltw - 1)); + interp_config_set_add_raw(&c0, true); // accumulator advances by the RAW step + interp_set_config(interp0, 0, &c0); + interp_config c1 = interp_default_config(); + interp_config_set_shift(&c1, (uint)(shy - ltw)); + interp_config_set_mask(&c1, (uint)ltw, (uint)(ltw + lth - 1)); + interp_config_set_add_raw(&c1, true); + interp_set_config(interp0, 1, &c1); + interp0->base[0] = (uint32_t)stepx; + interp0->base[1] = (uint32_t)stepy; + interp0->base[2] = (uint32_t)(uintptr_t)tex; + interp0->accum[0] = fx; + interp0->accum[1] = fy; + for (int i = 0; i < n; i++) { + const uint8_t *p = (const uint8_t *)interp0->pop[2]; + dst[i] = pal[*p]; + } +} + +#endif diff --git a/ports/raspberrypi/common-hal/socketpool/Socket.c b/ports/raspberrypi/common-hal/socketpool/Socket.c index 0543fd53dc3..840558d1ac3 100644 --- a/ports/raspberrypi/common-hal/socketpool/Socket.c +++ b/ports/raspberrypi/common-hal/socketpool/Socket.c @@ -1226,6 +1226,20 @@ int common_hal_socketpool_socket_setsockopt(socketpool_socket_obj_t *self, int l bool enable = optlen == sizeof(&zero) && memcmp(value, &zero, optlen); switch (level) { + case SOCKETPOOL_IPPROTO_IP: + switch (optname) { + case SOCKETPOOL_IP_MULTICAST_TTL: + if (self->type != SOCKETPOOL_SOCK_DGRAM) { + return -MP_EOPNOTSUPP; + } + if (self->pcb.udp == NULL || optlen < sizeof(uint8_t)) { + return -MP_EINVAL; + } + udp_set_multicast_ttl(self->pcb.udp, *(const uint8_t *)value); + return 0; + } + break; + case SOCKETPOOL_IPPROTO_TCP: switch (optname) { case SOCKETPOOL_TCP_NODELAY: diff --git a/ports/raspberrypi/common-hal/wiznet/PIO_SPI.c b/ports/raspberrypi/common-hal/wiznet/PIO_SPI.c new file mode 100755 index 00000000000..30fe8c5d25c --- /dev/null +++ b/ports/raspberrypi/common-hal/wiznet/PIO_SPI.c @@ -0,0 +1,123 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "bindings/wiznet/PIO_SPI.h" + +#include "shared/runtime/interrupt_char.h" +#include "py/mperrno.h" +#include "py/runtime.h" + +#include "supervisor/board.h" +#include "common-hal/microcontroller/Pin.h" +#include "shared-bindings/microcontroller/Pin.h" + +#include "hardware/dma.h" +#include "hardware/gpio.h" +#include "hardware/pio.h" +#include "hardware/clocks.h" + +#include "wizchip_pio_spi.h" + +#define NO_INSTANCE 0xff + +#ifndef PIO_SPI_PREFERRED_PIO +#define PIO_SPI_PREFERRED_PIO 1 +#endif + +// All wiznet spi operations must start with writing a 3 byte header + +wiznet_pio_spi_config_t wiznet_pio_spi_config; +wiznet_pio_spi_handle_t wiznet_pio_spi_handle = NULL; + +void common_hal_wiznet_pio_spi_construct(wiznet_pio_spi_obj_t *self, + const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, + const mcu_pin_obj_t *miso, bool half_duplex) { + + if (half_duplex) { + mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_half_duplex); + } + + wiznet_pio_spi_config.data_in_pin = miso->number; + wiznet_pio_spi_config.data_out_pin = mosi->number; + wiznet_pio_spi_config.clock_pin = clock->number; + + if (wiznet_pio_spi_handle != NULL) { + wiznet_pio_spi_close(wiznet_pio_spi_handle); + } + + wiznet_pio_spi_handle = wiznet_pio_spi_open(&wiznet_pio_spi_config); + (*wiznet_pio_spi_handle)->set_active(wiznet_pio_spi_handle); + +} + +bool common_hal_wiznet_pio_spi_deinited(wiznet_pio_spi_obj_t *self) { + return wiznet_pio_spi_config.clock_pin == 0; +} + +void common_hal_wiznet_pio_spi_deinit(wiznet_pio_spi_obj_t *self) { + if (common_hal_wiznet_pio_spi_deinited(self)) { + return; + } + + common_hal_reset_pin(self->clock); + common_hal_reset_pin(self->MOSI); + common_hal_reset_pin(self->MISO); + + wiznet_pio_spi_config.clock_pin = 0; +} + +bool common_hal_wiznet_pio_spi_configure(wiznet_pio_spi_obj_t *self, + uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) { + + uint32_t clock = clock_get_hz(clk_sys); + uint32_t clock_div = clock / baudrate; + + if (clock_div > clock / 4) { + clock_div = clock / 4; + } + + wiznet_pio_spi_config.clock_div_major = clock_div; + wiznet_pio_spi_config.clock_div_minor = 0; + + return true; +} + +bool common_hal_wiznet_pio_spi_try_lock(wiznet_pio_spi_obj_t *self) { + if (common_hal_wiznet_pio_spi_deinited(self)) { + return false; + } + + bool grabbed_lock = false; + if (!self->has_lock) { + grabbed_lock = true; + self->has_lock = true; + } + return grabbed_lock; +} + +bool common_hal_wiznet_pio_spi_has_lock(wiznet_pio_spi_obj_t *self) { + return self->has_lock; +} + +void common_hal_wiznet_pio_spi_unlock(wiznet_pio_spi_obj_t *self) { + self->has_lock = false; +} + +bool common_hal_wiznet_pio_spi_write(wiznet_pio_spi_obj_t *self, + const uint8_t *data, size_t len) { + wiznet_pio_spi_write_buffer(data, len); + return true; +} + +bool common_hal_wiznet_pio_spi_read(wiznet_pio_spi_obj_t *self, + uint8_t *data, size_t len, uint8_t write_value) { + wiznet_pio_spi_read_buffer(data, len); + return true; +} + +bool common_hal_wiznet_pio_spi_transfer(wiznet_pio_spi_obj_t *self, const uint8_t *data_out, uint8_t *data_in, size_t len) { + return wiznet_pio_spi_transfer(data_out, len, data_in, len); +} diff --git a/ports/raspberrypi/common-hal/wiznet/PIO_SPI.h b/ports/raspberrypi/common-hal/wiznet/PIO_SPI.h new file mode 100755 index 00000000000..6fcfb452cce --- /dev/null +++ b/ports/raspberrypi/common-hal/wiznet/PIO_SPI.h @@ -0,0 +1,25 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common-hal/microcontroller/Pin.h" + +#include "py/obj.h" + +#include "hardware/spi.h" + +#define SPI_HEADER_LEN 3 + +typedef struct { + mp_obj_base_t base; + bool has_lock; + const mcu_pin_obj_t *clock; + const mcu_pin_obj_t *MOSI; + const mcu_pin_obj_t *MISO; +} wiznet_pio_spi_obj_t; + +void reset_spi(void); diff --git a/ports/raspberrypi/common-hal/wiznet/__init__.c b/ports/raspberrypi/common-hal/wiznet/__init__.c new file mode 100755 index 00000000000..b3d5d23882d --- /dev/null +++ b/ports/raspberrypi/common-hal/wiznet/__init__.c @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Adafruit Industries LLC +// +// SPDX-License-Identifier: MIT + +// No wiznet module functions. diff --git a/ports/raspberrypi/common-hal/wiznet/wizchip_pio_spi.c b/ports/raspberrypi/common-hal/wiznet/wizchip_pio_spi.c new file mode 100755 index 00000000000..eb041527209 --- /dev/null +++ b/ports/raspberrypi/common-hal/wiznet/wizchip_pio_spi.c @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include "pico/stdlib.h" +#include "pico/error.h" + +#include "hardware/dma.h" +#include "hardware/clocks.h" + +#include "wizchip_pio_spi.h" +#include "wizchip_pio_spi.pio.h" + +#ifndef PIO_SPI_PREFERRED_PIO +#define PIO_SPI_PREFERRED_PIO 1 +#endif + +#define PADS_DRIVE_STRENGTH PADS_BANK0_GPIO0_DRIVE_VALUE_12MA +#define IRQ_SAMPLE_DELAY_NS 100 + +#define WIZNET_PIO_SPI_PROGRAM_NAME wiznet_pio_spi_write_read +#define WIZNET_PIO_SPI_PROGRAM_FUNC __CONCAT(WIZNET_PIO_SPI_PROGRAM_NAME, _program) +#define WIZNET_PIO_SPI_PROGRAM_GET_DEFAULT_CONFIG_FUNC __CONCAT(WIZNET_PIO_SPI_PROGRAM_NAME, _program_get_default_config) +#define WIZNET_PIO_SPI_OFFSET_WRITE_BITS __CONCAT(WIZNET_PIO_SPI_PROGRAM_NAME, _offset_write_bits) +#define WIZNET_PIO_SPI_OFFSET_WRITE_END __CONCAT(WIZNET_PIO_SPI_PROGRAM_NAME, _offset_write_end) +#define WIZNET_PIO_SPI_OFFSET_READ_END __CONCAT(WIZNET_PIO_SPI_PROGRAM_NAME, _offset_read_end) + +// All wiznet spi operations must start with writing a 3 byte header +#define WIZNET_PIO_SPI_HEADER_LEN 3 + +#ifndef WIZNET_PICO_PIO_SPI_INSTANCE_COUNT +#define WIZNET_PICO_PIO_SPI_INSTANCE_COUNT 1 +#endif + +typedef struct wiznet_pio_spi_state { + wiznet_pio_spi_funcs_t *funcs; + const wiznet_pio_spi_config_t *spi_config; + pio_hw_t *pio; + uint8_t pio_func_sel; + int8_t pio_offset; + int8_t pio_sm; + int8_t dma_out; + int8_t dma_in; + uint8_t spi_header[WIZNET_PIO_SPI_HEADER_LEN]; + uint8_t spi_header_count; +} wiznet_pio_spi_state_t; +static wiznet_pio_spi_state_t wiznet_pio_spi_state[WIZNET_PICO_PIO_SPI_INSTANCE_COUNT]; +static wiznet_pio_spi_state_t *active_state; + +static wiznet_pio_spi_funcs_t *get_wiznet_pio_spi_impl(void); + +// Initialise our gpios +static void wiznet_pio_spi_gpio_setup(wiznet_pio_spi_state_t *state) { + + // Setup MOSI, MISO and IRQ + gpio_init(state->spi_config->data_out_pin); + gpio_set_dir(state->spi_config->data_out_pin, GPIO_OUT); + gpio_put(state->spi_config->data_out_pin, false); + + gpio_init(state->spi_config->data_in_pin); + gpio_set_dir(state->spi_config->data_in_pin, GPIO_IN); + + // Setup CS + gpio_init(state->spi_config->cs_pin); + gpio_set_dir(state->spi_config->cs_pin, GPIO_OUT); + gpio_put(state->spi_config->cs_pin, true); + + // Setup IRQ + gpio_init(state->spi_config->irq_pin); + gpio_set_dir(state->spi_config->irq_pin, GPIO_IN); + gpio_set_pulls(state->spi_config->irq_pin, false, false); +} + +wiznet_pio_spi_handle_t wiznet_pio_spi_open(const wiznet_pio_spi_config_t *wiznet_pio_spi_config) { + wiznet_pio_spi_state_t *state; + for (size_t i = 0; i < count_of(wiznet_pio_spi_state); i++) { + if (!wiznet_pio_spi_state[i].funcs) { + state = &wiznet_pio_spi_state[i]; + break; + } + } + assert(state); + // if (!state) return NULL; + state->spi_config = wiznet_pio_spi_config; + state->funcs = get_wiznet_pio_spi_impl(); + + wiznet_pio_spi_gpio_setup(state); + + pio_hw_t *pios[2] = {pio0, pio1}; + uint pio_index = PIO_SPI_PREFERRED_PIO; + + if (!pio_can_add_program(pios[pio_index], &WIZNET_PIO_SPI_PROGRAM_FUNC)) { + pio_index ^= 1; + if (!pio_can_add_program(pios[pio_index], &WIZNET_PIO_SPI_PROGRAM_FUNC)) { + return NULL; + } + } + + state->pio = pios[pio_index]; + state->dma_in = -1; + state->dma_out = -1; + + static_assert(GPIO_FUNC_PIO1 == GPIO_FUNC_PIO0 + 1, ""); + state->pio_func_sel = GPIO_FUNC_PIO0 + pio_index; + state->pio_sm = (int8_t)pio_claim_unused_sm(state->pio, false); + if (state->pio_sm < 0) { + wiznet_pio_spi_close(&state->funcs); + return NULL; + } + + state->pio_offset = pio_add_program(state->pio, &WIZNET_PIO_SPI_PROGRAM_FUNC); + + pio_sm_config sm_config = WIZNET_PIO_SPI_PROGRAM_GET_DEFAULT_CONFIG_FUNC(state->pio_offset); + + sm_config_set_clkdiv_int_frac(&sm_config, state->spi_config->clock_div_major, state->spi_config->clock_div_minor); + hw_write_masked(&pads_bank0_hw->io[state->spi_config->clock_pin], + (uint)PADS_DRIVE_STRENGTH << PADS_BANK0_GPIO0_DRIVE_LSB, + PADS_BANK0_GPIO0_DRIVE_BITS + ); + hw_write_masked(&pads_bank0_hw->io[state->spi_config->clock_pin], + (uint)1 << PADS_BANK0_GPIO0_SLEWFAST_LSB, + PADS_BANK0_GPIO0_SLEWFAST_BITS + ); + + sm_config_set_out_pins(&sm_config, state->spi_config->data_out_pin, 1); + sm_config_set_in_pins(&sm_config, state->spi_config->data_in_pin); + sm_config_set_set_pins(&sm_config, state->spi_config->data_out_pin, 1); + sm_config_set_sideset(&sm_config, 1, false, false); + sm_config_set_sideset_pins(&sm_config, state->spi_config->clock_pin); + + sm_config_set_in_shift(&sm_config, false, true, 8); + sm_config_set_out_shift(&sm_config, false, true, 8); + hw_set_bits(&state->pio->input_sync_bypass, 1u << state->spi_config->data_in_pin); + pio_sm_set_config(state->pio, state->pio_sm, &sm_config); + pio_sm_set_consecutive_pindirs(state->pio, state->pio_sm, state->spi_config->clock_pin, 1, true); + gpio_set_function(state->spi_config->data_out_pin, state->pio_func_sel); + gpio_set_function(state->spi_config->clock_pin, state->pio_func_sel); + + // Set data pin to pull down and schmitt + gpio_set_pulls(state->spi_config->data_in_pin, false, true); + gpio_set_input_hysteresis_enabled(state->spi_config->data_in_pin, true); + + pio_sm_exec(state->pio, state->pio_sm, pio_encode_set(pio_pins, 1)); + state->dma_out = (int8_t)dma_claim_unused_channel(false); // todo: Should be able to use one dma channel? + state->dma_in = (int8_t)dma_claim_unused_channel(false); + if (state->dma_out < 0 || state->dma_in < 0) { + wiznet_pio_spi_close(&state->funcs); + return NULL; + } + return &state->funcs; +} + +void wiznet_pio_spi_close(wiznet_pio_spi_handle_t handle) { + wiznet_pio_spi_state_t *state = (wiznet_pio_spi_state_t *)handle; + if (state) { + if (state->pio_sm >= 0) { + if (state->pio_offset != -1) { + pio_remove_program(state->pio, &WIZNET_PIO_SPI_PROGRAM_FUNC, state->pio_offset); + } + + pio_sm_unclaim(state->pio, state->pio_sm); + } + if (state->dma_out >= 0) { + dma_channel_unclaim(state->dma_out); + state->dma_out = -1; + } + if (state->dma_in >= 0) { + dma_channel_unclaim(state->dma_in); + state->dma_in = -1; + } + state->funcs = NULL; + } +} + +static void cs_set(wiznet_pio_spi_state_t *state, bool value) { + gpio_put(state->spi_config->cs_pin, value); +} + +static __noinline void ns_delay(uint32_t ns) { + // cycles = ns * clk_sys_hz / 1,000,000,000 + uint32_t cycles = ns * (clock_get_hz(clk_sys) >> 16u) / (1000000000u >> 16u); + busy_wait_at_least_cycles(cycles); +} + +static void wiznet_pio_spi_frame_start(void) { + assert(active_state); + + gpio_set_function(active_state->spi_config->data_out_pin, active_state->pio_func_sel); + gpio_set_function(active_state->spi_config->clock_pin, active_state->pio_func_sel); + gpio_pull_down(active_state->spi_config->clock_pin); + + // Pull CS low + cs_set(active_state, false); +} + +static void wiznet_pio_spi_frame_end(void) { + assert(active_state); + + // from this point a positive edge will cause an IRQ to be pending + cs_set(active_state, true); + + // we need to wait a bit in case the irq line is incorrectly high + #ifdef IRQ_SAMPLE_DELAY_NS + ns_delay(IRQ_SAMPLE_DELAY_NS); + #endif +} + +// send tx then receive rx +// rx can be null if you just want to send, but tx and tx_length must be valid +bool wiznet_pio_spi_transfer(const uint8_t *tx, size_t tx_length, uint8_t *rx, size_t rx_length) { + const wiznet_pio_spi_state_t *state = active_state; + if (!state || (tx == NULL)) { + return false; + } + + if (rx != NULL && tx != NULL) { + assert(tx && tx_length && rx_length); + + pio_sm_set_enabled(state->pio, state->pio_sm, false); // disable sm + pio_sm_set_wrap(state->pio, state->pio_sm, state->pio_offset + WIZNET_PIO_SPI_OFFSET_WRITE_BITS, state->pio_offset + WIZNET_PIO_SPI_OFFSET_READ_END - 1); + pio_sm_clear_fifos(state->pio, state->pio_sm); // clear fifos from previous run + pio_sm_set_pindirs_with_mask(state->pio, state->pio_sm, 1u << state->spi_config->data_out_pin, 1u << state->spi_config->data_out_pin); + pio_sm_restart(state->pio, state->pio_sm); + pio_sm_clkdiv_restart(state->pio, state->pio_sm); + pio_sm_put(state->pio, state->pio_sm, tx_length * 8 - 1); // set x + pio_sm_exec(state->pio, state->pio_sm, pio_encode_out(pio_x, 32)); + pio_sm_put(state->pio, state->pio_sm, rx_length - 1); // set y + pio_sm_exec(state->pio, state->pio_sm, pio_encode_out(pio_y, 32)); + pio_sm_exec(state->pio, state->pio_sm, pio_encode_jmp(state->pio_offset)); // setup pc + + dma_channel_abort(state->dma_out); + dma_channel_abort(state->dma_in); + + dma_channel_config out_config = dma_channel_get_default_config(state->dma_out); + channel_config_set_dreq(&out_config, pio_get_dreq(state->pio, state->pio_sm, true)); + channel_config_set_transfer_data_size(&out_config, DMA_SIZE_8); + dma_channel_configure(state->dma_out, &out_config, &state->pio->txf[state->pio_sm], tx, tx_length, true); + + dma_channel_config in_config = dma_channel_get_default_config(state->dma_in); + channel_config_set_dreq(&in_config, pio_get_dreq(state->pio, state->pio_sm, false)); + channel_config_set_write_increment(&in_config, true); + channel_config_set_read_increment(&in_config, false); + channel_config_set_transfer_data_size(&in_config, DMA_SIZE_8); + dma_channel_configure(state->dma_in, &in_config, rx, &state->pio->rxf[state->pio_sm], rx_length, true); + + pio_sm_set_enabled(state->pio, state->pio_sm, true); + __compiler_memory_barrier(); + + dma_channel_wait_for_finish_blocking(state->dma_out); + dma_channel_wait_for_finish_blocking(state->dma_in); + + __compiler_memory_barrier(); + } else if (tx != NULL) { + assert(tx_length); + + pio_sm_set_enabled(state->pio, state->pio_sm, false); + pio_sm_set_wrap(state->pio, state->pio_sm, state->pio_offset + WIZNET_PIO_SPI_OFFSET_WRITE_BITS, state->pio_offset + WIZNET_PIO_SPI_OFFSET_WRITE_END - 1); + pio_sm_clear_fifos(state->pio, state->pio_sm); + pio_sm_restart(state->pio, state->pio_sm); + pio_sm_clkdiv_restart(state->pio, state->pio_sm); + pio_sm_put(state->pio, state->pio_sm, tx_length * 8 - 1); + pio_sm_exec(state->pio, state->pio_sm, pio_encode_out(pio_x, 32)); + pio_sm_put(state->pio, state->pio_sm, tx_length - 1); + pio_sm_exec(state->pio, state->pio_sm, pio_encode_out(pio_y, 32)); + pio_sm_exec(state->pio, state->pio_sm, pio_encode_set(pio_pins, 0)); + pio_sm_set_consecutive_pindirs(state->pio, state->pio_sm, state->spi_config->data_out_pin, 1, true); + pio_sm_exec(state->pio, state->pio_sm, pio_encode_jmp(state->pio_offset + WIZNET_PIO_SPI_OFFSET_WRITE_BITS)); + + dma_channel_abort(state->dma_out); + + dma_channel_config out_config = dma_channel_get_default_config(state->dma_out); + channel_config_set_dreq(&out_config, pio_get_dreq(state->pio, state->pio_sm, true)); + + channel_config_set_transfer_data_size(&out_config, DMA_SIZE_8); + dma_channel_configure(state->dma_out, &out_config, &state->pio->txf[state->pio_sm], tx, tx_length, true); + + const uint32_t fDebugTxStall = 1u << (PIO_FDEBUG_TXSTALL_LSB + state->pio_sm); + state->pio->fdebug = fDebugTxStall; + pio_sm_set_enabled(state->pio, state->pio_sm, true); + while (!(state->pio->fdebug & fDebugTxStall)) { + // printf("WIZNET_PIO_SPI: waiting for tx stall\n"); + tight_loop_contents(); // todo timeout + } + uint32_t timeout = 1000000; + if (timeout == 0) { + printf("ERROR: PIO TXSTALL timeout!\n"); + return false; + } + __compiler_memory_barrier(); + pio_sm_set_enabled(state->pio, state->pio_sm, false); + pio_sm_set_consecutive_pindirs(state->pio, state->pio_sm, state->spi_config->data_in_pin, 1, false); + } else if (rx != NULL) { + panic_unsupported(); // shouldn't be used + } + pio_sm_exec(state->pio, state->pio_sm, pio_encode_mov(pio_pins, pio_null)); // for next time we turn output on + + return true; +} + +// To read a byte we must first have been asked to write a 3 byte spi header +static uint8_t wiznet_pio_spi_read_byte(void) { + assert(active_state); + assert(active_state->spi_header_count == WIZNET_PIO_SPI_HEADER_LEN); + uint8_t ret; + if (!wiznet_pio_spi_transfer(active_state->spi_header, active_state->spi_header_count, &ret, 1)) { + panic("spi failed read"); + } + active_state->spi_header_count = 0; + return ret; +} + +// This is not used when the burst functions are provided +static void wiznet_pio_spi_write_byte(uint8_t wb) { + panic_unsupported(); // shouldn't be used +} + +// To read a buffer we must first have been asked to write a 3 byte spi header +void wiznet_pio_spi_read_buffer(uint8_t *pBuf, uint16_t len) { + assert(active_state); + assert(active_state->spi_header_count == WIZNET_PIO_SPI_HEADER_LEN); + if (!wiznet_pio_spi_transfer(active_state->spi_header, active_state->spi_header_count, pBuf, len)) { + panic("spi failed reading buffer"); + } + active_state->spi_header_count = 0; +} + +// If we have been asked to write a spi header already, then write it and the rest of the buffer +// or else if we've been given enough data for just the spi header, save it until the next call +// or we're writing a byte in which case we're given a buffer including the spi header +void wiznet_pio_spi_write_buffer(const uint8_t *pBuf, uint16_t len) { + assert(active_state); + + if (len < WIZNET_PIO_SPI_HEADER_LEN && active_state->spi_header_count != WIZNET_PIO_SPI_HEADER_LEN) { + memcpy(&active_state->spi_header[active_state->spi_header_count], pBuf, len); // expect another call + active_state->spi_header_count++; + } else if (len == WIZNET_PIO_SPI_HEADER_LEN && active_state->spi_header_count == 0) { + memcpy(active_state->spi_header, pBuf, WIZNET_PIO_SPI_HEADER_LEN); // expect another call + active_state->spi_header_count = WIZNET_PIO_SPI_HEADER_LEN; + } else { + if (active_state->spi_header_count == WIZNET_PIO_SPI_HEADER_LEN) { + if (!wiznet_pio_spi_transfer(active_state->spi_header, WIZNET_PIO_SPI_HEADER_LEN, NULL, 0)) { + panic("spi failed writing header"); + } + active_state->spi_header_count = 0; + } + assert(active_state->spi_header_count == 0); + if (!wiznet_pio_spi_transfer(pBuf, len, NULL, 0)) { + panic("spi failed writing buffer"); + } + } +} + +static void wiznet_pio_spi_set_active(wiznet_pio_spi_handle_t handle) { + active_state = (wiznet_pio_spi_state_t *)handle; +} + +static void wiznet_pio_spi_set_inactive(void) { + active_state = NULL; +} + +static void wiznet_pio_spi_reset(wiznet_pio_spi_handle_t handle) { + wiznet_pio_spi_state_t *state = (wiznet_pio_spi_state_t *)handle; + gpio_set_dir(state->spi_config->reset_pin, GPIO_OUT); + gpio_put(state->spi_config->reset_pin, 0); + sleep_ms(100); + gpio_put(state->spi_config->reset_pin, 1); + sleep_ms(100); +} + +static wiznet_pio_spi_funcs_t *get_wiznet_pio_spi_impl(void) { + static wiznet_pio_spi_funcs_t funcs = { + .close = wiznet_pio_spi_close, + .set_active = wiznet_pio_spi_set_active, + .set_inactive = wiznet_pio_spi_set_inactive, + .frame_start = wiznet_pio_spi_frame_start, + .frame_end = wiznet_pio_spi_frame_end, + .read_byte = wiznet_pio_spi_read_byte, + .write_byte = wiznet_pio_spi_write_byte, + .read_buffer = wiznet_pio_spi_read_buffer, + .write_buffer = wiznet_pio_spi_write_buffer, + .reset = wiznet_pio_spi_reset, + }; + return &funcs; +} diff --git a/ports/raspberrypi/common-hal/wiznet/wizchip_pio_spi.h b/ports/raspberrypi/common-hal/wiznet/wizchip_pio_spi.h new file mode 100755 index 00000000000..841eb48c480 --- /dev/null +++ b/ports/raspberrypi/common-hal/wiznet/wizchip_pio_spi.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _WIZNET_PIO_SPI_H_ +#define _WIZNET_PIO_SPI_H_ + +typedef struct wiznet_pio_spi_config { + uint8_t data_in_pin; + uint8_t data_out_pin; + uint8_t cs_pin; + uint8_t clock_pin; + uint8_t irq_pin; + uint8_t reset_pin; + uint16_t clock_div_major; + uint8_t clock_div_minor; + uint8_t spi_hw_instance; +} wiznet_pio_spi_config_t; + +typedef struct wiznet_pio_spi_funcs **wiznet_pio_spi_handle_t; + +typedef struct wiznet_pio_spi_funcs { + void (*close)(wiznet_pio_spi_handle_t funcs); + void (*set_active)(wiznet_pio_spi_handle_t funcs); + void (*set_inactive)(void); + void (*frame_start)(void); + void (*frame_end)(void); + uint8_t (*read_byte)(void); + void (*write_byte)(uint8_t tx_data); + void (*read_buffer)(uint8_t *pBuf, uint16_t len); + void (*write_buffer)(const uint8_t *pBuf, uint16_t len); + void (*reset)(wiznet_pio_spi_handle_t funcs); +} wiznet_pio_spi_funcs_t; + +wiznet_pio_spi_handle_t wiznet_pio_spi_open(const wiznet_pio_spi_config_t *pio_spi_config); +void wiznet_pio_spi_close(wiznet_pio_spi_handle_t handle); +bool wiznet_pio_spi_transfer(const uint8_t *tx, size_t tx_length, uint8_t *rx, size_t rx_length); +void wiznet_pio_spi_read_buffer(uint8_t *pBuf, uint16_t len); +void wiznet_pio_spi_write_buffer(const uint8_t *pBuf, uint16_t len); + +#endif diff --git a/ports/raspberrypi/common-hal/wiznet/wizchip_pio_spi.pio b/ports/raspberrypi/common-hal/wiznet/wizchip_pio_spi.pio new file mode 100755 index 00000000000..97a1c5b73d4 --- /dev/null +++ b/ports/raspberrypi/common-hal/wiznet/wizchip_pio_spi.pio @@ -0,0 +1,24 @@ +; +; Copyright (c) 2023 Raspberry Pi (Trading) Ltd. +; +; SPDX-License-Identifier: BSD-3-Clause +; + +.program wiznet_pio_spi_write_read +.side_set 1 + +public write_bits: + out pins, 1 side 0 + jmp x-- write_bits side 1 + set pins 0 side 0 +public write_end: +read_byte_delay: + set pindirs 0 side 0 +read_byte: + set x 6 side 1 +read_bits: + in pins, 1 side 0 + jmp x-- read_bits side 1 + in pins, 1 side 0 + jmp y-- read_byte side 0 +public read_end: diff --git a/ports/raspberrypi/mpconfigport.mk b/ports/raspberrypi/mpconfigport.mk index f4c6e809ba0..02fd6365b68 100644 --- a/ports/raspberrypi/mpconfigport.mk +++ b/ports/raspberrypi/mpconfigport.mk @@ -35,7 +35,6 @@ CIRCUITPY_FREQUENCYIO = 0 # Use PWM internally CIRCUITPY_I2CTARGET = 1 -CIRCUITPY_I2CIOEXPANDER = 1 CIRCUITPY_NVM = 1 # Use PIO internally CIRCUITPY_PULSEIO ?= 1 diff --git a/ports/raspberrypi/supervisor/port.c b/ports/raspberrypi/supervisor/port.c index 23a318454ed..34e9fc159fd 100644 --- a/ports/raspberrypi/supervisor/port.c +++ b/ports/raspberrypi/supervisor/port.c @@ -47,6 +47,7 @@ #include "supervisor/shared/stack.h" #include "supervisor/shared/tick.h" +#include "hardware/clocks.h" #include "hardware/structs/scb.h" #include "hardware/structs/watchdog.h" #include "hardware/gpio.h" @@ -152,6 +153,9 @@ static size_t _psram_size = 0; #include "hardware/structs/xip_ctrl.h" static void __no_inline_not_in_flash_func(setup_psram)(void) { + // Read the system clock before QMI goes into direct mode; clock_get_hz() is + // in flash and XIP is reconfigured below. + uint32_t sys_clk_khz = clock_get_hz(clk_sys) / 1000; gpio_set_function(CIRCUITPY_PSRAM_CHIP_SELECT->number, GPIO_FUNC_XIP_CS1); _psram_size = 0; common_hal_mcu_disable_interrupts(); @@ -237,14 +241,7 @@ static void __no_inline_not_in_flash_func(setup_psram)(void) { // Disable direct csr. qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS | QMI_DIRECT_CSR_EN_BITS); - qmi_hw->m[1].timing = - QMI_M0_TIMING_PAGEBREAK_VALUE_1024 << QMI_M0_TIMING_PAGEBREAK_LSB | // Break between pages. - 3 << QMI_M0_TIMING_SELECT_HOLD_LSB | // Delay releasing CS for 3 extra system cycles. - 1 << QMI_M0_TIMING_COOLDOWN_LSB | - 1 << QMI_M0_TIMING_RXDELAY_LSB | - 16 << QMI_M0_TIMING_MAX_SELECT_LSB | // In units of 64 system clock cycles. PSRAM says 8us max. 8 / 0.00752 / 64 = 16.62 - 7 << QMI_M0_TIMING_MIN_DESELECT_LSB | // In units of system clock cycles. PSRAM says 50ns.50 / 7.52 = 6.64 - 2 << QMI_M0_TIMING_CLKDIV_LSB; + mcu_processor_update_psram_timing(sys_clk_khz); qmi_hw->m[1].rfmt = (QMI_M0_RFMT_PREFIX_WIDTH_VALUE_Q << QMI_M0_RFMT_PREFIX_WIDTH_LSB | QMI_M0_RFMT_ADDR_WIDTH_VALUE_Q << QMI_M0_RFMT_ADDR_WIDTH_LSB | QMI_M0_RFMT_SUFFIX_WIDTH_VALUE_Q << QMI_M0_RFMT_SUFFIX_WIDTH_LSB | @@ -307,17 +304,17 @@ void port_heap_init(void) { } void *port_malloc(size_t size, bool dma_capable) { - if (!dma_capable && _psram_size > 0) { - common_hal_mcu_disable_interrupts(); - void *block = tlsf_malloc(_psram_heap, size); - common_hal_mcu_enable_interrupts(); - if (block) { - return block; - } - } + // Prefer internal RAM for everything: it is much faster than PSRAM + // (data there skips the external bus and the shared XIP cache). PSRAM, when + // present, serves as spillover capacity for allocations that don't need DMA. common_hal_mcu_disable_interrupts(); void *block = tlsf_malloc(_heap, size); common_hal_mcu_enable_interrupts(); + if (block == NULL && !dma_capable && _psram_size > 0) { + common_hal_mcu_disable_interrupts(); + block = tlsf_malloc(_psram_heap, size); + common_hal_mcu_enable_interrupts(); + } return block; } @@ -332,7 +329,11 @@ void port_free(void *ptr) { } void *port_realloc(void *ptr, size_t size, bool dma_capable) { - if (_psram_size > 0 && ((ptr != NULL && ((size_t)ptr) < SRAM_BASE) || (ptr == NULL && !dma_capable))) { + if (ptr == NULL) { + // Fresh allocation: same internal-first policy as port_malloc. + return port_malloc(size, dma_capable); + } + if (_psram_size > 0 && ((size_t)ptr) < SRAM_BASE) { common_hal_mcu_disable_interrupts(); void *block = tlsf_realloc(_psram_heap, ptr, size); common_hal_mcu_enable_interrupts(); diff --git a/ports/silabs/common-hal/_bleio/Adapter.c b/ports/silabs/common-hal/_bleio/Adapter.c index 14b1dac8207..4702555f47d 100644 --- a/ports/silabs/common-hal/_bleio/Adapter.c +++ b/ports/silabs/common-hal/_bleio/Adapter.c @@ -78,31 +78,16 @@ void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enabled) { const bool is_enabled = common_hal_bleio_adapter_get_enabled(self); - bd_addr get_address; - uint8_t address_type; uint8_t conn_index; bleio_connection_internal_t *connection; - sl_status_t sc = SL_STATUS_FAIL; - uint8_t device_name[DEVICE_NAME_LEN + 1]; - memset(device_name, 0, DEVICE_NAME_LEN); // Don't enable or disable twice if (is_enabled == enabled) { return; } - sc = sl_bt_system_get_identity_address(&get_address, &address_type); - if (SL_STATUS_OK != sc) { - mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Get address fail.")); - } - snprintf((char *)device_name, DEVICE_NAME_LEN + 1, - "CIRCUITPY-%X%X", get_address.addr[1], get_address.addr[0]); - if (enabled) { - sl_bt_gatt_server_write_attribute_value(gattdb_device_name, - 0, - DEVICE_NAME_LEN, - device_name); + bleio_adapter_reset_name(self); // Clear all of the internal connection objects. for (conn_index = 0; conn_index < BLEIO_TOTAL_CONNECTION_COUNT; conn_index++) { @@ -120,17 +105,17 @@ bleio_address_obj_t *common_hal_bleio_adapter_get_address(bleio_adapter_obj_t *s bd_addr get_address; uint8_t address_type; sl_status_t sc = SL_STATUS_FAIL; - bleio_address_obj_t *address; sc = sl_bt_system_get_identity_address(&get_address, &address_type); if (SL_STATUS_OK != sc) { return NULL; } - address = mp_obj_malloc(bleio_address_obj_t, &bleio_address_type); - common_hal_bleio_address_construct(address, get_address.addr, + // The cached address is refreshed on every call. + common_hal_bleio_address_construct(&self->address, get_address.addr, BLEIO_ADDRESS_TYPE_RANDOM_STATIC); - return address; + self->address.base.type = &bleio_address_type; + return &self->address; } // Set identity address @@ -138,17 +123,13 @@ bool common_hal_bleio_adapter_set_address(bleio_adapter_obj_t *self, bleio_address_obj_t *address) { sl_status_t sc = SL_STATUS_FAIL; - mp_buffer_info_t bufinfo; bd_addr ble_addr; if (NULL == address) { return false; } - if (!mp_get_buffer(address->bytes, &bufinfo, MP_BUFFER_READ)) { - return false; - } - memcpy(ble_addr.addr, bufinfo.buf, 6); - sl_bt_system_set_identity_address(ble_addr, PUBLIC_ADDRESS); + memcpy(ble_addr.addr, address->bytes, 6); + sc = sl_bt_system_set_identity_address(ble_addr, PUBLIC_ADDRESS); return sc == SL_STATUS_OK; } @@ -172,9 +153,13 @@ mp_obj_str_t *common_hal_bleio_adapter_get_name(bleio_adapter_obj_t *self) { // Set name of the BLE adapter void common_hal_bleio_adapter_set_name(bleio_adapter_obj_t *self, const char *name) { + size_t len = strlen(name); + if (len > DEVICE_NAME_LEN) { + len = DEVICE_NAME_LEN; + } sl_bt_gatt_server_write_attribute_value(gattdb_device_name, 0, - DEVICE_NAME_LEN, + len, (const uint8_t *)name); } @@ -453,10 +438,8 @@ bool common_hal_bleio_adapter_get_advertising(bleio_adapter_obj_t *self) { // Convert mac address of remote device to connect static void _convert_address(const bleio_address_obj_t *address, bd_addr *sd_address, uint8_t *addr_type) { - mp_buffer_info_t address_buf_info; *addr_type = address->type; - mp_get_buffer_raise(address->bytes, &address_buf_info, MP_BUFFER_READ); - memcpy(sd_address->addr, (uint8_t *)address_buf_info.buf, 6); + memcpy(sd_address->addr, address->bytes, 6); } // Add new connection into connection list @@ -640,11 +623,11 @@ void bleio_adapter_reset(bleio_adapter_obj_t *adapter) { // Wait up to 125 ms (128 ticks) for disconnect to complete. This should be // greater than most connection intervals. start_ticks = supervisor_ticks_ms64(); - while (any_connected && supervisor_ticks_ms64() - start_ticks < 128) { + do { any_connected = false; for (conn_index = 0; conn_index < BLEIO_TOTAL_CONNECTION_COUNT; conn_index++) { connection = &bleio_connections[conn_index]; any_connected |= connection->conn_handle != BLEIO_HANDLE_INVALID; } - } + } while (any_connected && supervisor_ticks_ms64() - start_ticks < 128); } diff --git a/ports/silabs/common-hal/_bleio/Adapter.h b/ports/silabs/common-hal/_bleio/Adapter.h index d7f252df330..561785bab3f 100644 --- a/ports/silabs/common-hal/_bleio/Adapter.h +++ b/ports/silabs/common-hal/_bleio/Adapter.h @@ -29,6 +29,7 @@ #include "py/obj.h" #include "py/objtuple.h" +#include "shared-bindings/_bleio/Address.h" #include "shared-bindings/_bleio/Connection.h" #include "shared-bindings/_bleio/ScanResults.h" #include "supervisor/background_callback.h" @@ -54,6 +55,10 @@ typedef struct { bool user_advertising; bool is_enable; uint8_t advertising_handle; + // Cached local address returned by common_hal_bleio_adapter_get_address(). + // Stored inline so it never needs to allocate, even when read before the + // heap is available (e.g. from bleio_adapter_reset_name). + bleio_address_obj_t address; } bleio_adapter_obj_t; typedef struct { diff --git a/ports/silabs/common-hal/_bleio/PacketBuffer.c b/ports/silabs/common-hal/_bleio/PacketBuffer.c index 881cf1622eb..291523e0a6d 100644 --- a/ports/silabs/common-hal/_bleio/PacketBuffer.c +++ b/ports/silabs/common-hal/_bleio/PacketBuffer.c @@ -387,6 +387,8 @@ bool common_hal_bleio_packet_buffer_deinited(bleio_packet_buffer_obj_t *self) { void common_hal_bleio_packet_buffer_deinit(bleio_packet_buffer_obj_t *self) { if (!common_hal_bleio_packet_buffer_deinited(self)) { ringbuf_deinit(&self->ringbuf); + // Mark as deinited, so common_hal_bleio_packet_buffer_deinited() reports it. + self->characteristic = NULL; } } diff --git a/ports/silabs/common-hal/_bleio/__init__.h b/ports/silabs/common-hal/_bleio/__init__.h index 63eda59d84f..4ecd6456b8b 100644 --- a/ports/silabs/common-hal/_bleio/__init__.h +++ b/ports/silabs/common-hal/_bleio/__init__.h @@ -61,12 +61,6 @@ // Maximum length for variable length Attribute Values. #define BLE_GATTS_VAR_ATTR_LEN_MAX (512) -// Track if the user code modified the BLE state -// to know if we need to undo it on reload. -extern bool vm_used_ble; - -// UUID shared by all CCCD's. -extern bleio_uuid_obj_t cccd_uuid; extern void bleio_reset(); extern osMutexId_t bluetooth_connection_mutex_id; diff --git a/ports/stm/boards/meowbit_v121/mpconfigboard.mk b/ports/stm/boards/meowbit_v121/mpconfigboard.mk index 419f073b5ea..271c4cd775c 100644 --- a/ports/stm/boards/meowbit_v121/mpconfigboard.mk +++ b/ports/stm/boards/meowbit_v121/mpconfigboard.mk @@ -31,6 +31,7 @@ CIRCUITPY_EPAPERDISPLAY = 0 CIRCUITPY_FRAMEBUFFERIO = 0 CIRCUITPY_I2CDISPLAYBUS = 0 CIRCUITPY_KEYPAD_DEMUX = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_PIXELMAP = 0 CIRCUITPY_SHARPDISPLAY = 0 CIRCUITPY_TILEPALETTEMAPPER = 0 diff --git a/ports/unix/variants/coverage/mpconfigvariant.mk b/ports/unix/variants/coverage/mpconfigvariant.mk index 682fe77459e..0dd93b25cf4 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.mk +++ b/ports/unix/variants/coverage/mpconfigvariant.mk @@ -39,6 +39,7 @@ SRC_BITMAP := \ shared-bindings/audiodelays/PitchShift.c \ shared-bindings/audiodelays/GranularPitchShift.c \ shared-bindings/audiodelays/MultiTapDelay.c \ + shared-bindings/audiodelays/Flanger.c \ shared-bindings/audiodelays/__init__.c \ shared-bindings/audiofilters/Distortion.c \ shared-bindings/audiofilters/Filter.c \ @@ -88,6 +89,7 @@ SRC_BITMAP := \ shared-module/audiodelays/PitchShift.c \ shared-module/audiodelays/GranularPitchShift.c \ shared-module/audiodelays/MultiTapDelay.c \ + shared-module/audiodelays/Flanger.c \ shared-module/audiodelays/__init__.c \ shared-module/audiofilters/Distortion.c \ shared-module/audiofilters/Filter.c \ diff --git a/ports/zephyr-cp/AGENTS.md b/ports/zephyr-cp/AGENTS.md index 58a3912e2ba..e8e240bde80 100644 --- a/ports/zephyr-cp/AGENTS.md +++ b/ports/zephyr-cp/AGENTS.md @@ -1,6 +1,7 @@ - Build a board by doing `make BOARD=_`. -- The corresponding configuration files are in `boards//` -- The files (not folders) in `boards/` directory are used by Zephyr. +- The corresponding configuration files are in `boards//`, including the + board's devicetree overlay (`board.overlay`) and Kconfig fragment (`board.conf`). The Makefile + passes them to Zephyr; the overlay replaces `app.overlay`, so include it if the board wants it. - To flash it on a board do `make BOARD=_ flash`. - Zephyr board docs are at `zephyr/boards//`. - Run zephyr-cp tests with `make test`. diff --git a/ports/zephyr-cp/Kconfig b/ports/zephyr-cp/Kconfig new file mode 100644 index 00000000000..015864100ec --- /dev/null +++ b/ports/zephyr-cp/Kconfig @@ -0,0 +1,104 @@ +# CircuitPython Zephyr port — application Kconfig +# +# Sources Zephyr's main Kconfig, then adds CircuitPython-specific defaults. +# Boards can override any of these in their boards/.conf file. + +osource "$(ZEPHYR_BASE)/Kconfig.zephyr" + +# ===== Peripheral defaults — enabled by default, boards can disable ===== + +config I2C + default y + +config SPI + default y + +config SPI_ASYNC + default y + +config I2S + default y + +config UART_LINE_CTRL + default y + +config ENTROPY_GENERATOR + default y + +# ===== Bluetooth defaults ===== + +# Use a variable for the chosen name so the comma isn't parsed as an argument separator +DT_BT_HCI_CHOSEN := zephyr,bt-hci + +config BT + default $(dt_chosen_enabled,$(DT_BT_HCI_CHOSEN)) + +config BT_PERIPHERAL + default y + +config BT_CENTRAL + default y + +config BT_BROADCASTER + default y + +config BT_OBSERVER + default y + +config BT_EXT_ADV + default y + +config BT_DEVICE_APPEARANCE_DYNAMIC + default y + +config BT_DEVICE_NAME_DYNAMIC + default y + +config BT_DEVICE_NAME_MAX + default 28 + +config BT_L2CAP_TX_MTU + default 253 + +# BT Buffers +config BT_BUF_CMD_TX_SIZE + default 255 + +config BT_BUF_EVT_RX_COUNT + default 16 + +config BT_BUF_EVT_RX_SIZE + default 255 + +config BT_BUF_ACL_TX_COUNT + default 3 + +config BT_BUF_ACL_TX_SIZE + default 251 + +config BT_BUF_ACL_RX_COUNT_EXTRA + default 1 + +config BT_BUF_ACL_RX_SIZE + default 255 + +config BT_GATT_DYNAMIC_DB + default y + +config BT_GATT_CLIENT + default y + +config BT_SMP + default y + +config BT_BONDABLE + default y + +config SETTINGS + default y + +config BT_SETTINGS + default y + +config BT_KEYS_OVERWRITE_OLDEST + default y diff --git a/ports/zephyr-cp/Makefile b/ports/zephyr-cp/Makefile index ab730a8b339..fbde791afa9 100644 --- a/ports/zephyr-cp/Makefile +++ b/ports/zephyr-cp/Makefile @@ -15,10 +15,33 @@ endif WEST_CMAKE_ARGS := -DZEPHYR_BOARD_ALIASES=$(CURDIR)/boards/board_aliases.cmake -Dzephyr-cp_TRANSLATION=$(TRANSLATION) +# Board files live in the board's own folder as board.overlay / board.conf. Zephyr only auto-applies +# boards/.overlay and boards/.conf named after the RESOLVED Zephyr board, so it finds +# neither those (wrong directory) nor files named after a board declared through cp_board_alias() +# (wrong name) - the build silently produces a different firmware instead of warning. Pass them +# explicitly when they exist. +CP_BOARD_DIR := $(CURDIR)/boards/$(firstword $(subst _, ,$(BOARD)))/$(patsubst $(firstword $(subst _, ,$(BOARD)))_%,%,$(BOARD)) +# DTC_OVERLAY_FILE, not EXTRA_: a board overlay REPLACES app.overlay (Zephyr only falls back to +# app.overlay when no board overlay is found), which is why the board overlays here include it +# themselves - and why one board that has no USB deliberately does not. Adding the overlay instead +# would force app.overlay on every board. socs/ ships .conf only, so nothing else is displaced. +CP_BOARD_OVERLAY := $(wildcard $(CP_BOARD_DIR)/board.overlay) +ifneq ($(CP_BOARD_OVERLAY),) +WEST_CMAKE_ARGS += -Dzephyr-cp_DTC_OVERLAY_FILE=$(CP_BOARD_OVERLAY) +endif +CP_BOARD_CONF := $(wildcard $(CP_BOARD_DIR)/board.conf) + # When DEBUG=1, apply additional Kconfig fragments for debug-friendly settings. DEBUG_CONF_FILE ?= $(CURDIR)/debug.conf ifeq ($(DEBUG),1) -WEST_CMAKE_ARGS += -Dzephyr-cp_EXTRA_CONF_FILE=$(DEBUG_CONF_FILE) +ifneq ($(CP_BOARD_CONF),) +CP_BOARD_CONF := $(CP_BOARD_CONF);$(DEBUG_CONF_FILE) +else +CP_BOARD_CONF := $(DEBUG_CONF_FILE) +endif +endif +ifneq ($(CP_BOARD_CONF),) +WEST_CMAKE_ARGS += -Dzephyr-cp_EXTRA_CONF_FILE=$(CP_BOARD_CONF) endif .PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash recover debug debug-jlink debugserver attach run run-sim clean menuconfig all clean-all sim clean-sim test fetch-port-submodules @@ -42,6 +65,9 @@ $(BUILD)/firmware.exe: $(BUILD)/zephyr-cp/zephyr/zephyr.elf $(BUILD)/firmware.uf2: $(BUILD)/zephyr-cp/zephyr/zephyr.elf cp $(BUILD)/zephyr-cp/zephyr/zephyr.uf2 $@ +$(BUILD)/firmware.rps: $(BUILD)/zephyr-cp/zephyr/zephyr.elf + cp $(BUILD)/zephyr-cp/zephyr/zephyr.rps $@ + flash: $(BUILD)/zephyr-cp/zephyr/zephyr.elf west flash -d $(BUILD) diff --git a/ports/zephyr-cp/app.overlay b/ports/zephyr-cp/app.overlay index 0f34d64a80f..bb2be8924f6 100644 --- a/ports/zephyr-cp/app.overlay +++ b/ports/zephyr-cp/app.overlay @@ -8,3 +8,15 @@ label = "CircuitPython Data"; }; }; + +/ { + hid_dev_0: hid_dev_0 { + compatible = "zephyr,hid-device"; + label = "HID0"; + protocol-code = "none"; + in-polling-period-us = <1000>; + in-report-size = <64>; + out-polling-period-us = <1000>; + out-report-size = <64>; + }; +}; diff --git a/ports/zephyr-cp/boards/adafruit/clue_nrf52840_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/clue_nrf52840_zephyr/autogen_board_info.toml new file mode 100644 index 00000000000..0afea836c10 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit/clue_nrf52840_zephyr/autogen_board_info.toml @@ -0,0 +1,129 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Adafruit Industries LLC CLUE nRF52840 Express" + +[modules] +__future__ = true +_bleio = true # Zephyr board has _bleio +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilewriter = false +audiofilters = false +audiofreeverb = false +audioi2sin = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has displayio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = true +gifio = true # Zephyr board has busio +gnss = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = true # Zephyr board has busio +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = true # Zephyr board has nvm +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = true # Zephyr board has flash +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_audio = false +usb_cdc = true +usb_hid = true +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = true # Zephyr board has zephyr_display +zephyr_kernel = false +zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/adafruit/clue_nrf52840_zephyr/board.conf b/ports/zephyr-cp/boards/adafruit/clue_nrf52840_zephyr/board.conf new file mode 100644 index 00000000000..cfa31e2fdcf --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit/clue_nrf52840_zephyr/board.conf @@ -0,0 +1,6 @@ +CONFIG_USE_DT_CODE_PARTITION=y + +CONFIG_BOARD_SERIAL_BACKEND_CDC_ACM=n + +# Enable the ST7789V TFT via the Zephyr display subsystem +CONFIG_DISPLAY=y diff --git a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.overlay b/ports/zephyr-cp/boards/adafruit/clue_nrf52840_zephyr/board.overlay similarity index 81% rename from ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.overlay rename to ports/zephyr-cp/boards/adafruit/clue_nrf52840_zephyr/board.overlay index e01ebd6df1f..586165dfe06 100644 --- a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.overlay +++ b/ports/zephyr-cp/boards/adafruit/clue_nrf52840_zephyr/board.overlay @@ -23,16 +23,19 @@ &flash0 { partitions { code_partition: partition@26000 { + compatible = "zephyr,mapped-partition"; label = "Application"; reg = <0x00026000 0x000c4000>; }; storage_partition: partition@ea000 { + compatible = "zephyr,mapped-partition"; label = "storage"; reg = <0x000ea000 0x00008000>; }; nvm_partition: partition@f2000 { + compatible = "zephyr,mapped-partition"; label = "nvm"; reg = <0x000f2000 0x00002000>; }; @@ -43,4 +46,4 @@ status = "okay"; }; -#include "../app.overlay" +#include "../../../app.overlay" diff --git a/ports/zephyr-cp/boards/adafruit/clue_nrf52840_zephyr/circuitpython.toml b/ports/zephyr-cp/boards/adafruit/clue_nrf52840_zephyr/circuitpython.toml new file mode 100644 index 00000000000..2d7b8c3db48 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit/clue_nrf52840_zephyr/circuitpython.toml @@ -0,0 +1,4 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["hex", "uf2"] +USB_VID=0x239A +USB_PID=0x8072 +NAME="CLUE nRF52840 Express" diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/autogen_board_info.toml index d6aca312226..459ee6a98c2 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/autogen_board_info.toml @@ -3,7 +3,7 @@ name = "Adafruit Industries LLC Feather Bluefruit Sense" [modules] __future__ = true -_bleio = false +_bleio = true # Zephyr board has _bleio _eve = false _pew = false _pixelmap = false @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = false zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/board.conf b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/board.conf new file mode 100644 index 00000000000..6d7299ec6e6 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/board.conf @@ -0,0 +1,3 @@ +CONFIG_USE_DT_CODE_PARTITION=y + +CONFIG_BOARD_SERIAL_BACKEND_CDC_ACM=n diff --git a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.overlay b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/board.overlay similarity index 81% rename from ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.overlay rename to ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/board.overlay index e01ebd6df1f..586165dfe06 100644 --- a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.overlay +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/board.overlay @@ -23,16 +23,19 @@ &flash0 { partitions { code_partition: partition@26000 { + compatible = "zephyr,mapped-partition"; label = "Application"; reg = <0x00026000 0x000c4000>; }; storage_partition: partition@ea000 { + compatible = "zephyr,mapped-partition"; label = "storage"; reg = <0x000ea000 0x00008000>; }; nvm_partition: partition@f2000 { + compatible = "zephyr,mapped-partition"; label = "nvm"; reg = <0x000f2000 0x00002000>; }; @@ -43,4 +46,4 @@ status = "okay"; }; -#include "../app.overlay" +#include "../../../app.overlay" diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml index dafe7811779..654a6c2e01b 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml @@ -3,7 +3,7 @@ name = "Adafruit Industries LLC Feather nRF52840 Express" [modules] __future__ = true -_bleio = false +_bleio = true # Zephyr board has _bleio _eve = false _pew = false _pixelmap = false @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = false zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/board.conf b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/board.conf new file mode 100644 index 00000000000..6d7299ec6e6 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/board.conf @@ -0,0 +1,3 @@ +CONFIG_USE_DT_CODE_PARTITION=y + +CONFIG_BOARD_SERIAL_BACKEND_CDC_ACM=n diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/board.overlay b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/board.overlay new file mode 100644 index 00000000000..586165dfe06 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/board.overlay @@ -0,0 +1,49 @@ +/ { + chosen { + zephyr,console = &uart0; + zephyr,shell-uart = &uart0; + zephyr,uart-mcumgr = &uart0; + zephyr,bt-mon-uart = &uart0; + zephyr,bt-c2h-uart = &uart0; + }; +}; + +&zephyr_udc0 { + /delete-node/ board_cdc_acm_uart; +}; + + +&gd25q16 { + /delete-node/ partitions; +}; + +/delete-node/ &storage_partition; +/delete-node/ &code_partition; + +&flash0 { + partitions { + code_partition: partition@26000 { + compatible = "zephyr,mapped-partition"; + label = "Application"; + reg = <0x00026000 0x000c4000>; + }; + + storage_partition: partition@ea000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x000ea000 0x00008000>; + }; + + nvm_partition: partition@f2000 { + compatible = "zephyr,mapped-partition"; + label = "nvm"; + reg = <0x000f2000 0x00002000>; + }; + }; +}; + +&uart0 { + status = "okay"; +}; + +#include "../../../app.overlay" diff --git a/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml index cecf8e55a83..17022b4d2c7 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = false zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/adafruit_feather_rp2040.conf b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/board.conf similarity index 100% rename from ports/zephyr-cp/boards/adafruit_feather_rp2040.conf rename to ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/board.conf diff --git a/ports/zephyr-cp/boards/adafruit_feather_rp2040.overlay b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/board.overlay similarity index 61% rename from ports/zephyr-cp/boards/adafruit_feather_rp2040.overlay rename to ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/board.overlay index af7295ffe35..10cc674a66a 100644 --- a/ports/zephyr-cp/boards/adafruit_feather_rp2040.overlay +++ b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/board.overlay @@ -2,33 +2,42 @@ /delete-node/ partitions; partitions { - compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; /* Reserved memory for the second stage bootloader */ second_stage_bootloader: partition@0 { + compatible = "zephyr,mapped-partition"; label = "second_stage_bootloader"; reg = <0x00000000 0x100>; read-only; }; code_partition: partition@100 { + compatible = "zephyr,mapped-partition"; label = "code-partition"; reg = <0x100 (0x180000 - 0x100)>; read-only; }; nvm_partition: partition@180000 { + compatible = "zephyr,mapped-partition"; label = "nvm"; - reg = <0x180000 0x1000>; + reg = <0x180000 0x800>; + }; + + storage_partition: partition@180800 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x180800 0x800>; }; circuitpy_partition: partition@181000 { + compatible = "zephyr,mapped-partition"; label = "circuitpy"; reg = <0x181000 (DT_SIZE_M(8) - 0x181000)>; }; }; }; -#include "../app.overlay" +#include "../../../app.overlay" diff --git a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.conf b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.conf deleted file mode 100644 index 20176b34be0..00000000000 --- a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.conf +++ /dev/null @@ -1,10 +0,0 @@ -CONFIG_BT=y -CONFIG_BT_PERIPHERAL=y -CONFIG_BT_CENTRAL=y -CONFIG_BT_BROADCASTER=y -CONFIG_BT_OBSERVER=y -CONFIG_BT_EXT_ADV=y - -CONFIG_USE_DT_CODE_PARTITION=y - -CONFIG_BOARD_SERIAL_BACKEND_CDC_ACM=n diff --git a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.conf b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.conf deleted file mode 100644 index 20176b34be0..00000000000 --- a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.conf +++ /dev/null @@ -1,10 +0,0 @@ -CONFIG_BT=y -CONFIG_BT_PERIPHERAL=y -CONFIG_BT_CENTRAL=y -CONFIG_BT_BROADCASTER=y -CONFIG_BT_OBSERVER=y -CONFIG_BT_EXT_ADV=y - -CONFIG_USE_DT_CODE_PARTITION=y - -CONFIG_BOARD_SERIAL_BACKEND_CDC_ACM=n diff --git a/ports/zephyr-cp/boards/board_aliases.cmake b/ports/zephyr-cp/boards/board_aliases.cmake index ad0c1b5a57a..29ce83ceb5e 100644 --- a/ports/zephyr-cp/boards/board_aliases.cmake +++ b/ports/zephyr-cp/boards/board_aliases.cmake @@ -28,6 +28,7 @@ endmacro() cp_board_alias(pca10056 nrf52840dk/nrf52840) cp_board_alias(adafruit_feather_nrf52840_zephyr adafruit_feather_nrf52840/nrf52840/uf2) +cp_board_alias(adafruit_clue_nrf52840_zephyr adafruit_clue_nrf52840/nrf52840/uf2) cp_board_alias(adafruit_feather_nrf52840_sense_zephyr adafruit_feather_nrf52840/nrf52840/sense/uf2) cp_board_alias(adafruit_feather_rp2040_zephyr adafruit_feather_rp2040/rp2040) cp_board_alias(renesas_ek_ra6m5 ek_ra6m5) @@ -35,7 +36,10 @@ cp_board_alias(renesas_ek_ra8d1 ek_ra8d1) cp_board_alias(renesas_da14695_dk_usb da14695_dk_usb) cp_board_alias(native_native_sim native_sim/native) cp_board_alias(native_nrf5340bsim nrf5340bsim/nrf5340/cpuapp) +cp_board_alias(native_nrf54lm20bsim nrf54lm20bsim/nrf54lm20a/cpuapp) cp_board_alias(nordic_nrf54l15dk nrf54l15dk/nrf54l15/cpuapp) +cp_board_alias(nordic_nrf54l15tag nrf54l15tag/nrf54l15/cpuapp) +cp_board_alias(nordic_nrf54lm20dk nrf54lm20dk/nrf54lm20a/cpuapp) cp_board_alias(nordic_nrf54h20dk nrf54h20dk/nrf54h20/cpuapp) cp_board_alias(nordic_nrf5340dk nrf5340dk/nrf5340/cpuapp) cp_board_alias(nordic_nrf7002dk nrf7002dk/nrf5340/cpuapp) @@ -51,3 +55,4 @@ cp_board_alias(raspberrypi_rpi_pico_w_zephyr rpi_pico/rp2040/w) cp_board_alias(raspberrypi_rpi_pico2_zephyr rpi_pico2/rp2350a/m33) cp_board_alias(raspberrypi_rpi_pico2_w_zephyr rpi_pico2/rp2350a/m33/w) cp_board_alias(st_nucleo_n657x0_q nucleo_n657x0_q/stm32n657xx) +cp_board_alias(silabs_siwx917_dk2605a siwx917_dk2605a) diff --git a/ports/zephyr-cp/boards/da14695_dk_usb.conf b/ports/zephyr-cp/boards/da14695_dk_usb.conf deleted file mode 100644 index 145a9393407..00000000000 --- a/ports/zephyr-cp/boards/da14695_dk_usb.conf +++ /dev/null @@ -1,20 +0,0 @@ -CONFIG_BT=y -CONFIG_BT_PERIPHERAL=y -CONFIG_BT_CENTRAL=y -CONFIG_BT_BROADCASTER=y -CONFIG_BT_OBSERVER=y -CONFIG_BT_EXT_ADV=y - -CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=y -CONFIG_BT_DEVICE_NAME_DYNAMIC=y -CONFIG_BT_DEVICE_NAME_MAX=28 -CONFIG_BT_L2CAP_TX_MTU=253 - -# BT Buffers -CONFIG_BT_BUF_CMD_TX_SIZE=255 -CONFIG_BT_BUF_EVT_RX_COUNT=16 -CONFIG_BT_BUF_EVT_RX_SIZE=255 -CONFIG_BT_BUF_ACL_TX_COUNT=3 -CONFIG_BT_BUF_ACL_TX_SIZE=251 -CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=1 -CONFIG_BT_BUF_ACL_RX_SIZE=255 diff --git a/ports/zephyr-cp/boards/da14695_dk_usb.overlay b/ports/zephyr-cp/boards/da14695_dk_usb.overlay deleted file mode 100644 index fbc1817c759..00000000000 --- a/ports/zephyr-cp/boards/da14695_dk_usb.overlay +++ /dev/null @@ -1,10 +0,0 @@ -&flash0 { - partitions{ - circuitpy_partition: partition@118000 { - label = "circuitpy"; - reg = <0x118000 (DT_SIZE_M(4) - DT_SIZE_K(1120))>; - }; - }; -}; - -#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/ek_ra8d1.conf b/ports/zephyr-cp/boards/ek_ra8d1.conf deleted file mode 100644 index f979d31e751..00000000000 --- a/ports/zephyr-cp/boards/ek_ra8d1.conf +++ /dev/null @@ -1,3 +0,0 @@ - -# Enable Zephyr display subsystem so DT chosen zephyr,display creates a device. -CONFIG_DISPLAY=y diff --git a/ports/zephyr-cp/boards/frdm_mcxn947_mcxn947_cpu0.overlay b/ports/zephyr-cp/boards/frdm_mcxn947_mcxn947_cpu0.overlay deleted file mode 100644 index 83242201c5b..00000000000 --- a/ports/zephyr-cp/boards/frdm_mcxn947_mcxn947_cpu0.overlay +++ /dev/null @@ -1,5 +0,0 @@ -&w25q64jvssiq { - /delete-node/ partitions; -}; - -#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/frdm_rw612.overlay b/ports/zephyr-cp/boards/frdm_rw612.overlay deleted file mode 100644 index c6a021d9999..00000000000 --- a/ports/zephyr-cp/boards/frdm_rw612.overlay +++ /dev/null @@ -1,11 +0,0 @@ -&w25q512jvfiq { - partitions { - /delete-node/ partition@620000; - circuitpy_partition: partition@620000 { - label = "circuitpy"; - reg = <0x00620000 (DT_SIZE_M(58) - DT_SIZE_K(128))>; - }; - }; -}; - -#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/frdm_rw612_rw612_cpu0.overlay b/ports/zephyr-cp/boards/frdm_rw612_rw612_cpu0.overlay deleted file mode 100644 index 9c517e43255..00000000000 --- a/ports/zephyr-cp/boards/frdm_rw612_rw612_cpu0.overlay +++ /dev/null @@ -1,12 +0,0 @@ -&w25q512jvfiq { - partitions { - /delete-node/ storage_partition; - circuitpy_partition: partition@620000 { - label = "circuitpy"; - reg = <0x00620000 (DT_SIZE_M(58) - DT_SIZE_K(128))>; - }; - } - -}; - -#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.overlay b/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.overlay deleted file mode 100644 index ac6fdd8654e..00000000000 --- a/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.overlay +++ /dev/null @@ -1,15 +0,0 @@ -&is25wp128 { - partitions { - /delete-node/ partition@e20000; - circuitpy_partition: partition@e20000 { - label = "circuitpy"; - reg = <0x00e20000 (DT_SIZE_M(2) - DT_SIZE_K(128))>; - }; - }; -}; - -&sai1 { - mclk-output; -}; - -#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml index b565e8b139f..69a8dad30d8 100644 --- a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml @@ -1,5 +1,5 @@ # This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. -name = "POSIX/Native Boards Native simulator - native_sim" +name = "zephyr Native simulator - native_sim" [modules] __future__ = true @@ -18,8 +18,10 @@ atexit = false audiobusio = true # Zephyr board has audiobusio audiocore = true # Zephyr board has audiobusio audiodelays = true # Zephyr board has audiobusio +audiofilewriter = false audiofilters = true # Zephyr board has audiobusio audiofreeverb = true # Zephyr board has audiobusio +audioi2sin = false audioio = false audiomixer = true # Zephyr board has audiobusio audiomp3 = true # Zephyr board has audiobusio @@ -105,6 +107,7 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = false usb_hid = false usb_host = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = true # Zephyr board has zephyr_display zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/native_sim.conf b/ports/zephyr-cp/boards/native/native_sim/board.conf similarity index 94% rename from ports/zephyr-cp/boards/native_sim.conf rename to ports/zephyr-cp/boards/native/native_sim/board.conf index 00af0d01ac6..6ae4c35110b 100644 --- a/ports/zephyr-cp/boards/native_sim.conf +++ b/ports/zephyr-cp/boards/native/native_sim/board.conf @@ -1,3 +1,6 @@ +# No Bluetooth hardware on native_sim +CONFIG_BT=n + CONFIG_EMUL=y CONFIG_GPIO=y CONFIG_NATIVE_SIM_SLOWDOWN_TO_REAL_TIME=n diff --git a/ports/zephyr-cp/boards/native_sim.overlay b/ports/zephyr-cp/boards/native/native_sim/board.overlay similarity index 77% rename from ports/zephyr-cp/boards/native_sim.overlay rename to ports/zephyr-cp/boards/native/native_sim/board.overlay index aee9d17f9d0..d59a13db80d 100644 --- a/ports/zephyr-cp/boards/native_sim.overlay +++ b/ports/zephyr-cp/boards/native/native_sim/board.overlay @@ -25,16 +25,23 @@ &flash0 { /delete-node/ partitions; partitions { - compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; circuitpy_partition: partition@0 { + compatible = "zephyr,mapped-partition"; label = "circuitpy"; - reg = <0x00000000 DT_SIZE_K(2040)>; + reg = <0x00000000 DT_SIZE_K(2032)>; + }; + + storage_partition: partition@1fc000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x001fc000 DT_SIZE_K(8)>; }; nvm_partition: partition@1fe000 { + compatible = "zephyr,mapped-partition"; label = "nvm"; reg = <0x001fe000 0x00002000>; }; @@ -53,4 +60,4 @@ }; }; -#include "../app.overlay" +#include "../../../app.overlay" diff --git a/ports/zephyr-cp/boards/native/native_sim/circuitpython.toml b/ports/zephyr-cp/boards/native/native_sim/circuitpython.toml index fbda7c563b8..7177c825f17 100644 --- a/ports/zephyr-cp/boards/native/native_sim/circuitpython.toml +++ b/ports/zephyr-cp/boards/native/native_sim/circuitpython.toml @@ -1 +1 @@ -CIRCUITPY_BUILD_EXTENSIONS = ["elf", "exe"] +CIRCUITPY_BUILD_EXTENSIONS = ["exe"] diff --git a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml index aa723b53f64..47edeed3b59 100644 --- a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml @@ -1,5 +1,5 @@ # This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. -name = "POSIX/Native Boards nRF5340 simulated boards (BabbleSim)" +name = "zephyr nRF5340 simulated boards (BabbleSim)" [modules] __future__ = true @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,6 +107,7 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = false usb_hid = false usb_host = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = false zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf b/ports/zephyr-cp/boards/native/nrf5340bsim/board.conf similarity index 53% rename from ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf rename to ports/zephyr-cp/boards/native/nrf5340bsim/board.conf index 57628a61e20..2579bf28d67 100644 --- a/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/board.conf @@ -4,26 +4,11 @@ CONFIG_GPIO=y # Enable Bluetooth stack - bsim is for BT simulation -CONFIG_BT=y CONFIG_BT_HCI=y -CONFIG_BT_PERIPHERAL=y -CONFIG_BT_CENTRAL=y -CONFIG_BT_OBSERVER=y -CONFIG_BT_BROADCASTER=y - -CONFIG_BT_L2CAP_TX_MTU=253 -CONFIG_BT_BUF_CMD_TX_COUNT=2 -CONFIG_BT_BUF_CMD_TX_SIZE=255 CONFIG_BT_HCI_VS=y -CONFIG_BT_BUF_EVT_RX_COUNT=16 -CONFIG_BT_BUF_EVT_RX_SIZE=255 -CONFIG_BT_BUF_ACL_TX_COUNT=3 -CONFIG_BT_BUF_ACL_TX_SIZE=251 -CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=1 -CONFIG_BT_BUF_ACL_RX_SIZE=255 -CONFIG_BT_DEVICE_NAME_DYNAMIC=y -CONFIG_BT_DEVICE_NAME_MAX=28 +# Override Kconfig default +CONFIG_BT_BUF_CMD_TX_COUNT=2 # Ensure the network core image starts when using native simulator CONFIG_NATIVE_SIMULATOR_AUTOSTART_MCU=y diff --git a/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.overlay b/ports/zephyr-cp/boards/native/nrf5340bsim/board.overlay similarity index 64% rename from ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.overlay rename to ports/zephyr-cp/boards/native/nrf5340bsim/board.overlay index eeb043c6f3c..5f0c9fd7af8 100644 --- a/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.overlay +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/board.overlay @@ -14,13 +14,19 @@ &flash0 { /delete-node/ partitions; partitions { - compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; circuitpy_partition: partition@0 { + compatible = "zephyr,mapped-partition"; label = "circuitpy"; - reg = <0x00000000 DT_SIZE_K(1024)>; + reg = <0x00000000 DT_SIZE_K(1008)>; + }; + + storage_partition: partition@fc000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x000fc000 DT_SIZE_K(16)>; }; }; }; diff --git a/ports/zephyr-cp/boards/native/nrf5340bsim/circuitpython.toml b/ports/zephyr-cp/boards/native/nrf5340bsim/circuitpython.toml index 3272dd4c5f3..7177c825f17 100644 --- a/ports/zephyr-cp/boards/native/nrf5340bsim/circuitpython.toml +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/circuitpython.toml @@ -1 +1 @@ -CIRCUITPY_BUILD_EXTENSIONS = ["elf"] +CIRCUITPY_BUILD_EXTENSIONS = ["exe"] diff --git a/ports/zephyr-cp/boards/native/nrf54lm20bsim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/nrf54lm20bsim/autogen_board_info.toml new file mode 100644 index 00000000000..d1feffc835d --- /dev/null +++ b/ports/zephyr-cp/boards/native/nrf54lm20bsim/autogen_board_info.toml @@ -0,0 +1,129 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "zephyr nRF54LM20 simulated boards (BabbleSim)" + +[modules] +__future__ = true +_bleio = true # Zephyr board has _bleio +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilewriter = false +audiofilters = false +audiofreeverb = false +audioi2sin = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = true +gifio = true # Zephyr board has busio +gnss = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = true # Zephyr board has busio +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = true +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_audio = false +usb_cdc = false +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = false +zephyr_kernel = false +zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/native/nrf54lm20bsim/board.conf b/ports/zephyr-cp/boards/native/nrf54lm20bsim/board.conf new file mode 100644 index 00000000000..e2a9595c2a3 --- /dev/null +++ b/ports/zephyr-cp/boards/native/nrf54lm20bsim/board.conf @@ -0,0 +1,27 @@ +# Configuration for nrf54lm20bsim simulated board + +CONFIG_GPIO=y + +# Enable Bluetooth stack - bsim is for BT simulation +CONFIG_BT_HCI=y +CONFIG_BT_HCI_VS=y + +# Match nrf54lm20dk hardware: support dynamic TX power control +CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL=y + +# Override Kconfig default +CONFIG_BT_BUF_CMD_TX_COUNT=2 + +# The bsim native_sim does not drive the BLE controller ticker time-slot +# mechanism used for radio-synchronized flash operations. With the default +# SOC_FLASH_NRF_RADIO_SYNC_TICKER, flash erase/write waits on a semaphore +# that is never given, hanging for ~34s (FLASH_TIMEOUT_MS) per operation. +# Disable radio sync so flash operations run directly, matching nrf5340bsim +# (which uses an IPC-based controller and so defaults to _NONE). +CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE=y + +CONFIG_TRACING=y +CONFIG_TRACING_PERFETTO=y +CONFIG_TRACING_SYNC=y +CONFIG_TRACING_BACKEND_POSIX=y +CONFIG_TRACING_GPIO=y diff --git a/ports/zephyr-cp/boards/native/nrf54lm20bsim/board.overlay b/ports/zephyr-cp/boards/native/nrf54lm20bsim/board.overlay new file mode 100644 index 00000000000..ed5831ec5a3 --- /dev/null +++ b/ports/zephyr-cp/boards/native/nrf54lm20bsim/board.overlay @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +/ { + sram0: memory@20000000 { + device_type = "memory"; + compatible = "zephyr,memory-region", "mmio-sram"; + reg = <0x20000000 DT_SIZE_K(511)>; + zephyr,memory-region = "SRAM"; + }; + + chosen { + zephyr,sram = &sram0; + }; +}; + +&cpuapp_rram { + /delete-node/ partitions; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + circuitpy_partition: partition@0 { + compatible = "zephyr,mapped-partition"; + label = "circuitpy"; + reg = <0x00000000 (DT_SIZE_K(2036) - DT_SIZE_K(32))>; + }; + + storage_partition: partition@1f5000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x001f5000 DT_SIZE_K(32)>; + }; + }; +}; + +/* Note: bsim doesn't have USB, so we don't include app.overlay */ diff --git a/ports/zephyr-cp/boards/native/nrf54lm20bsim/circuitpython.toml b/ports/zephyr-cp/boards/native/nrf54lm20bsim/circuitpython.toml new file mode 100644 index 00000000000..7177c825f17 --- /dev/null +++ b/ports/zephyr-cp/boards/native/nrf54lm20bsim/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["exe"] diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml index ee20efa6fee..366d4cd4cb3 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml @@ -18,8 +18,10 @@ atexit = false audiobusio = true # Zephyr board has audiobusio audiocore = true # Zephyr board has audiobusio audiodelays = true # Zephyr board has audiobusio +audiofilewriter = false audiofilters = true # Zephyr board has audiobusio audiofreeverb = true # Zephyr board has audiobusio +audioi2sin = false audioio = false audiomixer = true # Zephyr board has audiobusio audiomp3 = true # Zephyr board has audiobusio @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = false zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.overlay b/ports/zephyr-cp/boards/nordic/nrf5340dk/board.overlay similarity index 65% rename from ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.overlay rename to ports/zephyr-cp/boards/nordic/nrf5340dk/board.overlay index eb899df8cc9..6b7ee85de7c 100644 --- a/ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.overlay +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/board.overlay @@ -21,4 +21,12 @@ i2s_rxtx: &i2s0 { clock-source = "ACLK"; }; -#include "../app.overlay" +/* Remove slot1 (OTA), expand slot0 to use the space. + * CircuitPython doesn't use OTA updates. */ +&slot0_partition { + reg = <0x00010000 0x000E0000>; +}; + +/delete-node/ &slot1_partition; + +#include "../../../app.overlay" diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/circuitpython.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/circuitpython.toml index bf17d26d91b..cc7e3ae8c4a 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/circuitpython.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/circuitpython.toml @@ -1,3 +1,3 @@ -CIRCUITPY_BUILD_EXTENSIONS = ["elf"] +CIRCUITPY_BUILD_EXTENSIONS = ["hex"] USB_VID=0x239A USB_PID=0x8166 diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml index ca2c90ceb0b..b3da39f698c 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = false zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.conf b/ports/zephyr-cp/boards/nordic/nrf54h20dk/board.conf similarity index 78% rename from ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.conf rename to ports/zephyr-cp/boards/nordic/nrf54h20dk/board.conf index a55b90c50e7..3aca8d77007 100644 --- a/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.conf +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/board.conf @@ -1,5 +1,8 @@ CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE=4096 +# BLE overflows flash on this board. +CONFIG_BT=n + # Reduce flash usage for this board. CONFIG_LOG=y CONFIG_LOG_MAX_LEVEL=2 diff --git a/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/ports/zephyr-cp/boards/nordic/nrf54h20dk/board.overlay similarity index 76% rename from ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay rename to ports/zephyr-cp/boards/nordic/nrf54h20dk/board.overlay index a70eede551e..e557179992b 100644 --- a/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/board.overlay @@ -44,4 +44,12 @@ status = "okay"; }; -#include "../app.overlay" +/* Remove slot1 (OTA), expand slot0 to use the space. + * CircuitPython doesn't use OTA updates. */ +&slot0_partition { + reg = <0x40000 DT_SIZE_K(656)>; +}; + +/delete-node/ &slot1_partition; + +#include "../../../app.overlay" diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/circuitpython.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/circuitpython.toml index 415c471b3d4..7cf2501b2b1 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54h20dk/circuitpython.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/circuitpython.toml @@ -1,2 +1,2 @@ -CIRCUITPY_BUILD_EXTENSIONS = ["elf"] +CIRCUITPY_BUILD_EXTENSIONS = ["elf", "hex"] DISABLED_MODULES=["jpegio", "gifio", "tilepalettemapper"] diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml index 69fdc2da903..51551341199 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml @@ -3,7 +3,7 @@ name = "Nordic Semiconductor nRF54L15 DK" [modules] __future__ = true -_bleio = false +_bleio = true # Zephyr board has _bleio _eve = false _pew = false _pixelmap = false @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,6 +107,7 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = false usb_hid = false usb_host = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = false zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/board.overlay b/ports/zephyr-cp/boards/nordic/nrf54l15dk/board.overlay new file mode 100644 index 00000000000..7ffd2dec4d6 --- /dev/null +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/board.overlay @@ -0,0 +1,28 @@ +// nRF54L15 DK doesn't have USB, so no app.overlay for CDC ACM. + +// I2C bus on P1.8 (SDA) and P1.10 (SCL). +&pinctrl { + i2c21_default: i2c21_default { + group1 { + psels = , + ; + bias-pull-up; + }; + }; + + i2c21_sleep: i2c21_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; +}; + +&i2c21 { + clock-frequency = ; + pinctrl-0 = <&i2c21_default>; + pinctrl-1 = <&i2c21_sleep>; + pinctrl-names = "default", "sleep"; + status = "okay"; +}; diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/circuitpython.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/circuitpython.toml index 3272dd4c5f3..00c797b1dad 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/circuitpython.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/circuitpython.toml @@ -1 +1 @@ -CIRCUITPY_BUILD_EXTENSIONS = ["elf"] +CIRCUITPY_BUILD_EXTENSIONS = ["elf", "hex"] diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15tag/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15tag/autogen_board_info.toml new file mode 100644 index 00000000000..8c19b7801b5 --- /dev/null +++ b/ports/zephyr-cp/boards/nordic/nrf54l15tag/autogen_board_info.toml @@ -0,0 +1,129 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Nordic Semiconductor nRF54L15 TAG" + +[modules] +__future__ = true +_bleio = true # Zephyr board has _bleio +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilewriter = false +audiofilters = false +audiofreeverb = false +audioi2sin = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = true +gifio = true # Zephyr board has busio +gnss = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = true # Zephyr board has busio +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = true +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_audio = false +usb_cdc = false +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = false +zephyr_kernel = false +zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15tag/board.conf b/ports/zephyr-cp/boards/nordic/nrf54l15tag/board.conf new file mode 100644 index 00000000000..2627ebe2e5f --- /dev/null +++ b/ports/zephyr-cp/boards/nordic/nrf54l15tag/board.conf @@ -0,0 +1,4 @@ +# Enable UART console (not enabled in the Zephyr board defconfig) +CONFIG_SERIAL=y +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15tag/board.overlay b/ports/zephyr-cp/boards/nordic/nrf54l15tag/board.overlay new file mode 100644 index 00000000000..ddf5c8f7e8f --- /dev/null +++ b/ports/zephyr-cp/boards/nordic/nrf54l15tag/board.overlay @@ -0,0 +1,39 @@ +// No app.overlay because it doesn't have USB. + +// The nrf54l15tag Zephyr board doesn't enable a UART or set zephyr,console, +// unlike the nrf54l15dk. Set up UART30 as the console using available pins. +&uart30 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart30_default>; + pinctrl-1 = <&uart30_sleep>; + pinctrl-names = "default", "sleep"; +}; + +&pinctrl { + uart30_default: uart30_default { + group1 { + psels = ; + }; + group2 { + psels = ; + bias-pull-up; + }; + }; + + uart30_sleep: uart30_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; +}; + +/ { + chosen { + zephyr,console = &uart30; + zephyr,shell-uart = &uart30; + zephyr,uart-mcumgr = &uart30; + }; +}; diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15tag/circuitpython.toml b/ports/zephyr-cp/boards/nordic/nrf54l15tag/circuitpython.toml new file mode 100644 index 00000000000..83e6bcd39c4 --- /dev/null +++ b/ports/zephyr-cp/boards/nordic/nrf54l15tag/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["hex"] diff --git a/ports/zephyr-cp/boards/nordic/nrf54lm20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54lm20dk/autogen_board_info.toml new file mode 100644 index 00000000000..9da6422265e --- /dev/null +++ b/ports/zephyr-cp/boards/nordic/nrf54lm20dk/autogen_board_info.toml @@ -0,0 +1,129 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Nordic Semiconductor nRF54LM20 DK" + +[modules] +__future__ = true +_bleio = true # Zephyr board has _bleio +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilewriter = false +audiofilters = false +audiofreeverb = false +audioi2sin = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = true +gifio = true # Zephyr board has busio +gnss = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = true # Zephyr board has busio +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = true # Zephyr board has flash +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_audio = false +usb_cdc = true +usb_hid = true +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = false +zephyr_kernel = false +zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/nordic/nrf54lm20dk/board.conf b/ports/zephyr-cp/boards/nordic/nrf54lm20dk/board.conf new file mode 100644 index 00000000000..19f8cea27a2 --- /dev/null +++ b/ports/zephyr-cp/boards/nordic/nrf54lm20dk/board.conf @@ -0,0 +1,10 @@ +CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL=y + +# Work around a latent bug in soc_flash_nrf_ticker.c: with the default +# NRF_RRAM_WRITE_BUFFER_SIZE=1, FLASH_SLOT_WRITE (500 us) < +# FLASH_SYNC_SWITCHING_TIME (1700 us), so `interval = duration - +# FLASH_SYNC_SWITCHING_TIME` underflows and the next flash slot is scheduled +# ~71 minutes out. Any write larger than a single 16-byte slot then times out +# (-ETIMEDOUT / -116), e.g. persisting bond keys right after pairing. +# See zephyr/tests/boards/nrf/rram/overlay-radio_sync.conf. +CONFIG_NRF_RRAM_WRITE_BUFFER_SIZE=32 diff --git a/ports/zephyr-cp/boards/nordic/nrf54lm20dk/board.overlay b/ports/zephyr-cp/boards/nordic/nrf54lm20dk/board.overlay new file mode 100644 index 00000000000..0f48a5a5d46 --- /dev/null +++ b/ports/zephyr-cp/boards/nordic/nrf54lm20dk/board.overlay @@ -0,0 +1,52 @@ +// nRF54LM20 DK has USB. Include the main app overlay for USB CDC ACM console and data. +#include "../../../app.overlay" + +// Link application starting at 0x0 instead of slot0_partition (no mcuboot yet). +/ { + chosen { + zephyr,code-partition = &cpuapp_rram; + }; +}; + +// Enable the external MX25R6435F 8MB QSPI flash (connected via SPIM00). +&mx25r64 { + status = "okay"; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + circuitpy_partition: partition@0 { + label = "circuitpy"; + reg = <0x00000000 DT_SIZE_M(8)>; + }; + }; +}; + +// I2C bus on P1.13 (SDA) and P1.23 (SCL). +&pinctrl { + i2c21_default: i2c21_default { + group1 { + psels = , + ; + bias-pull-up; + }; + }; + + i2c21_sleep: i2c21_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; +}; + +&i2c21 { + clock-frequency = ; + pinctrl-0 = <&i2c21_default>; + pinctrl-1 = <&i2c21_sleep>; + pinctrl-names = "default", "sleep"; + status = "okay"; +}; diff --git a/ports/zephyr-cp/boards/nordic/nrf54lm20dk/circuitpython.toml b/ports/zephyr-cp/boards/nordic/nrf54lm20dk/circuitpython.toml new file mode 100644 index 00000000000..83e6bcd39c4 --- /dev/null +++ b/ports/zephyr-cp/boards/nordic/nrf54lm20dk/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["hex"] diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index e0b759e01ce..9f31476b449 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -3,7 +3,7 @@ name = "Nordic Semiconductor nRF7002 DK" [modules] __future__ = true -_bleio = true # Zephyr board has _bleio +_bleio = false _eve = false _pew = false _pixelmap = false @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = true # Zephyr board has wifi zephyr_display = false zephyr_kernel = false zlib = false +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/board.conf b/ports/zephyr-cp/boards/nordic/nrf7002dk/board.conf new file mode 100644 index 00000000000..2255bd760d3 --- /dev/null +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/board.conf @@ -0,0 +1,10 @@ +CONFIG_NETWORKING=y +CONFIG_WIFI=y +CONFIG_WIFI_NM_WPA_SUPPLICANT_LEGACY_CRYPTO=n + +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y + +CONFIG_LOG=n +CONFIG_ASSERT=n +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_BT=n diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/board.overlay b/ports/zephyr-cp/boards/nordic/nrf7002dk/board.overlay new file mode 100644 index 00000000000..30eb519f93b --- /dev/null +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/board.overlay @@ -0,0 +1,14 @@ +/* Remove slot1 (OTA), expand slot0 to use the space. + * CircuitPython doesn't use OTA updates. */ +&slot0_partition { + /* Shrink mcuboot to 48KB, absorb slot1 + gap before storage */ + reg = <0x0000C000 0x000EC000>; +}; + +&boot_partition { + reg = <0x00000000 0x0000C000>; +}; + +/delete-node/ &slot1_partition; + +#include "../../../app.overlay" diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/circuitpython.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/circuitpython.toml index 761d2631477..badaffb8e1b 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/circuitpython.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/circuitpython.toml @@ -1,5 +1,12 @@ -CIRCUITPY_BUILD_EXTENSIONS = ["elf"] +CIRCUITPY_BUILD_EXTENSIONS = ["hex"] USB_VID=0x239A USB_PID=0x8168 BLOBS=["nrf_wifi"] DISABLED_MODULES=["aesio", "adafruit_bus_device", "zlib", "jpegio", "tilepalettemapper", "gifio"] + +# ulab is on by default. This board has under 3 KB of flash headroom, and +# ulab costs about 90 KB, so it opts out. +CIRCUITPY_ULAB = false + +# The same 3 KB is why radio.ping() is off here too. +CIRCUITPY_WIFI_PING = false diff --git a/ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.conf b/ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.conf deleted file mode 100644 index 145a9393407..00000000000 --- a/ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.conf +++ /dev/null @@ -1,20 +0,0 @@ -CONFIG_BT=y -CONFIG_BT_PERIPHERAL=y -CONFIG_BT_CENTRAL=y -CONFIG_BT_BROADCASTER=y -CONFIG_BT_OBSERVER=y -CONFIG_BT_EXT_ADV=y - -CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=y -CONFIG_BT_DEVICE_NAME_DYNAMIC=y -CONFIG_BT_DEVICE_NAME_MAX=28 -CONFIG_BT_L2CAP_TX_MTU=253 - -# BT Buffers -CONFIG_BT_BUF_CMD_TX_SIZE=255 -CONFIG_BT_BUF_EVT_RX_COUNT=16 -CONFIG_BT_BUF_EVT_RX_SIZE=255 -CONFIG_BT_BUF_ACL_TX_COUNT=3 -CONFIG_BT_BUF_ACL_TX_SIZE=251 -CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=1 -CONFIG_BT_BUF_ACL_RX_SIZE=255 diff --git a/ports/zephyr-cp/boards/nrf54l15dk_nrf54l15_cpuapp.overlay b/ports/zephyr-cp/boards/nrf54l15dk_nrf54l15_cpuapp.overlay deleted file mode 100644 index 39db1e981af..00000000000 --- a/ports/zephyr-cp/boards/nrf54l15dk_nrf54l15_cpuapp.overlay +++ /dev/null @@ -1 +0,0 @@ -// No app.overlay because it doesn't have USB. diff --git a/ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf b/ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf deleted file mode 100644 index 91c956fa676..00000000000 --- a/ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf +++ /dev/null @@ -1,19 +0,0 @@ -CONFIG_NETWORKING=y -CONFIG_WIFI=y - -CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y - -CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=y -CONFIG_BT_DEVICE_NAME_DYNAMIC=y -CONFIG_BT_DEVICE_NAME_MAX=28 - -CONFIG_BT=y -CONFIG_BT_PERIPHERAL=y -CONFIG_BT_CENTRAL=y -CONFIG_BT_BROADCASTER=y -CONFIG_BT_OBSERVER=y -CONFIG_BT_EXT_ADV=y - -CONFIG_LOG=n -CONFIG_ASSERT=n -CONFIG_TEST_RANDOM_GENERATOR=y diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml index b8e2c0601aa..273aff2376d 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml @@ -18,8 +18,10 @@ atexit = false audiobusio = true # Zephyr board has audiobusio audiocore = true # Zephyr board has audiobusio audiodelays = true # Zephyr board has audiobusio +audiofilewriter = false audiofilters = true # Zephyr board has audiobusio audiofreeverb = true # Zephyr board has audiobusio +audioi2sin = false audioio = false audiomixer = true # Zephyr board has audiobusio audiomp3 = true # Zephyr board has audiobusio @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = false zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/frdm_mcxn947_mcxn947_cpu0.conf b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/board.conf similarity index 100% rename from ports/zephyr-cp/boards/frdm_mcxn947_mcxn947_cpu0.conf rename to ports/zephyr-cp/boards/nxp/frdm_mcxn947/board.conf diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/board.overlay b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/board.overlay new file mode 100644 index 00000000000..3eef280f220 --- /dev/null +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/board.overlay @@ -0,0 +1,23 @@ +&w25q64jvssiq { + /delete-node/ partitions; + + partitions { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + storage_partition: partition@0 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x0 DT_SIZE_K(32)>; + }; + + circuitpy_partition: partition@8000 { + compatible = "zephyr,mapped-partition"; + label = "circuitpy"; + reg = <0x00008000 (DT_SIZE_M(8) - DT_SIZE_K(32))>; + }; + }; +}; + +#include "../../../app.overlay" diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml index 557b4f0448f..6efb741df94 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = true # Zephyr board has wifi zephyr_display = false zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/frdm_rw612.conf b/ports/zephyr-cp/boards/nxp/frdm_rw612/board.conf similarity index 53% rename from ports/zephyr-cp/boards/frdm_rw612.conf rename to ports/zephyr-cp/boards/nxp/frdm_rw612/board.conf index c06f78ae830..f8d1dfa0ad6 100644 --- a/ports/zephyr-cp/boards/frdm_rw612.conf +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/board.conf @@ -4,6 +4,7 @@ CONFIG_NET_DHCPV4=y CONFIG_NET_SOCKETS=y CONFIG_WIFI=y +CONFIG_WIFI_NM_WPA_SUPPLICANT_LEGACY_CRYPTO=n CONFIG_NET_L2_WIFI_MGMT=y CONFIG_NET_MGMT_EVENT=y CONFIG_NET_MGMT_EVENT_INFO=y @@ -18,25 +19,7 @@ CONFIG_MBEDTLS_CIPHERSUITE_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256=y CONFIG_MBEDTLS_ENTROPY_C=y CONFIG_MBEDTLS_CTR_DRBG_C=y -CONFIG_BT=y -CONFIG_BT_PERIPHERAL=y -CONFIG_BT_CENTRAL=y -CONFIG_BT_BROADCASTER=y -CONFIG_BT_OBSERVER=y -CONFIG_BT_EXT_ADV=y - -CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=y -CONFIG_BT_DEVICE_NAME_DYNAMIC=y -CONFIG_BT_DEVICE_NAME_MAX=28 -CONFIG_BT_L2CAP_TX_MTU=253 - -# BT Buffers -CONFIG_BT_BUF_CMD_TX_SIZE=255 -CONFIG_BT_BUF_EVT_RX_COUNT=16 -CONFIG_BT_BUF_EVT_RX_SIZE=255 +# Override Kconfig default (3) CONFIG_BT_BUF_ACL_TX_COUNT=8 -CONFIG_BT_BUF_ACL_TX_SIZE=251 -CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=1 -CONFIG_BT_BUF_ACL_RX_SIZE=255 CONFIG_UDC_WORKQUEUE_STACK_SIZE=1024 diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/board.overlay b/ports/zephyr-cp/boards/nxp/frdm_rw612/board.overlay new file mode 100644 index 00000000000..9153fd433ab --- /dev/null +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/board.overlay @@ -0,0 +1,17 @@ +&w25q512jvfiq { + partitions { + /delete-node/ partition@620000; + storage_partition: partition@620000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x00620000 DT_SIZE_K(32)>; + }; + circuitpy_partition: partition@628000 { + compatible = "zephyr,mapped-partition"; + label = "circuitpy"; + reg = <0x00628000 (DT_SIZE_M(58) - DT_SIZE_K(128) - DT_SIZE_K(32))>; + }; + }; +}; + +#include "../../../app.overlay" diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml index ee43ef86e8c..cf5c0650015 100644 --- a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml @@ -18,8 +18,10 @@ atexit = false audiobusio = true # Zephyr board has audiobusio audiocore = true # Zephyr board has audiobusio audiodelays = true # Zephyr board has audiobusio +audiofilewriter = false audiofilters = true # Zephyr board has audiobusio audiofreeverb = true # Zephyr board has audiobusio +audioi2sin = false audioio = false audiomixer = true # Zephyr board has audiobusio audiomp3 = true # Zephyr board has audiobusio @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = false zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.conf b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/board.conf similarity index 100% rename from ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.conf rename to ports/zephyr-cp/boards/nxp/mimxrt1170_evk/board.conf diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/board.overlay b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/board.overlay new file mode 100644 index 00000000000..cd5f0089494 --- /dev/null +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/board.overlay @@ -0,0 +1,21 @@ +&is25wp128 { + partitions { + /delete-node/ partition@e20000; + storage_partition: partition@e20000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x00e20000 DT_SIZE_K(32)>; + }; + circuitpy_partition: partition@e28000 { + compatible = "zephyr,mapped-partition"; + label = "circuitpy"; + reg = <0x00e28000 (DT_SIZE_M(2) - DT_SIZE_K(128) - DT_SIZE_K(32))>; + }; + }; +}; + +&sai1 { + mclk-output; +}; + +#include "../../../app.overlay" diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml index 2215bcf4135..e7d3f4502fd 100644 --- a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = true # Zephyr board has wifi zephyr_display = false zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.conf b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/board.conf similarity index 90% rename from ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.conf rename to ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/board.conf index d75b1cd20a9..8fcc1c0b9ae 100644 --- a/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.conf +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/board.conf @@ -4,6 +4,7 @@ CONFIG_NET_DHCPV4=y CONFIG_NET_SOCKETS=y CONFIG_WIFI=y +CONFIG_WIFI_NM_WPA_SUPPLICANT_LEGACY_CRYPTO=n CONFIG_NET_L2_WIFI_MGMT=y CONFIG_NET_MGMT_EVENT=y CONFIG_NET_MGMT_EVENT_INFO=y diff --git a/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33.overlay b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/board.overlay similarity index 52% rename from ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33.overlay rename to ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/board.overlay index eb94fe7de67..c35cb42519a 100644 --- a/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33.overlay +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/board.overlay @@ -1,25 +1,33 @@ &flash0 { partitions { - compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; code_partition: partition@0 { + compatible = "zephyr,mapped-partition"; label = "code-partition"; reg = <0x0 0x180000>; read-only; }; nvm_partition: partition@180000 { + compatible = "zephyr,mapped-partition"; label = "nvm"; - reg = <0x180000 0x1000>; + reg = <0x180000 0x800>; + }; + + storage_partition: partition@180800 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x180800 0x800>; }; circuitpy_partition: partition@181000 { + compatible = "zephyr,mapped-partition"; label = "circuitpy"; reg = <0x181000 (DT_SIZE_M(4) - 0x181000)>; }; }; }; -#include "../app.overlay" +#include "../../../app.overlay" diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml index 61324042092..95a2873af70 100644 --- a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = false zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.overlay b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/board.overlay similarity index 52% rename from ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.overlay rename to ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/board.overlay index eb94fe7de67..c35cb42519a 100644 --- a/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.overlay +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/board.overlay @@ -1,25 +1,33 @@ &flash0 { partitions { - compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; code_partition: partition@0 { + compatible = "zephyr,mapped-partition"; label = "code-partition"; reg = <0x0 0x180000>; read-only; }; nvm_partition: partition@180000 { + compatible = "zephyr,mapped-partition"; label = "nvm"; - reg = <0x180000 0x1000>; + reg = <0x180000 0x800>; + }; + + storage_partition: partition@180800 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x180800 0x800>; }; circuitpy_partition: partition@181000 { + compatible = "zephyr,mapped-partition"; label = "circuitpy"; reg = <0x181000 (DT_SIZE_M(4) - 0x181000)>; }; }; }; -#include "../app.overlay" +#include "../../../app.overlay" diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml index 203f5fd7048..e2036293823 100644 --- a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = true # Zephyr board has wifi zephyr_display = false zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/rpi_pico_rp2040_w.conf b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/board.conf similarity index 91% rename from ports/zephyr-cp/boards/rpi_pico_rp2040_w.conf rename to ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/board.conf index 975d6bfd6e1..1e2d7ae1cd2 100644 --- a/ports/zephyr-cp/boards/rpi_pico_rp2040_w.conf +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/board.conf @@ -4,6 +4,7 @@ CONFIG_NET_DHCPV4=y CONFIG_NET_SOCKETS=y CONFIG_WIFI=y +CONFIG_WIFI_NM_WPA_SUPPLICANT_LEGACY_CRYPTO=n CONFIG_NET_L2_WIFI_MGMT=y CONFIG_NET_MGMT_EVENT=y CONFIG_NET_MGMT_EVENT_INFO=y diff --git a/ports/zephyr-cp/boards/rpi_pico_rp2040.overlay b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/board.overlay similarity index 61% rename from ports/zephyr-cp/boards/rpi_pico_rp2040.overlay rename to ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/board.overlay index ce9083dd62d..f1228e10138 100644 --- a/ports/zephyr-cp/boards/rpi_pico_rp2040.overlay +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/board.overlay @@ -2,33 +2,42 @@ /delete-node/ partitions; partitions { - compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; /* Reserved memory for the second stage bootloader */ second_stage_bootloader: partition@0 { + compatible = "zephyr,mapped-partition"; label = "second_stage_bootloader"; reg = <0x00000000 0x100>; read-only; }; code_partition: partition@100 { + compatible = "zephyr,mapped-partition"; label = "code-partition"; reg = <0x100 (0x180000 - 0x100)>; read-only; }; nvm_partition: partition@180000 { + compatible = "zephyr,mapped-partition"; label = "nvm"; - reg = <0x180000 0x1000>; + reg = <0x180000 0x800>; + }; + + storage_partition: partition@180800 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x180800 0x800>; }; circuitpy_partition: partition@181000 { + compatible = "zephyr,mapped-partition"; label = "circuitpy"; reg = <0x181000 (DT_SIZE_M(2) - 0x181000)>; }; }; }; -#include "../app.overlay" +#include "../../../app.overlay" diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml index 13c77bf2928..3c96b7d4f57 100644 --- a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = false zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/rpi_pico_rp2040_w.overlay b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/board.overlay similarity index 60% rename from ports/zephyr-cp/boards/rpi_pico_rp2040_w.overlay rename to ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/board.overlay index ce9083dd62d..16b3251df5a 100644 --- a/ports/zephyr-cp/boards/rpi_pico_rp2040_w.overlay +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/board.overlay @@ -2,33 +2,43 @@ /delete-node/ partitions; partitions { - compatible = "fixed-partitions"; + ranges; #address-cells = <1>; #size-cells = <1>; /* Reserved memory for the second stage bootloader */ second_stage_bootloader: partition@0 { + compatible = "zephyr,mapped-partition"; label = "second_stage_bootloader"; reg = <0x00000000 0x100>; read-only; }; code_partition: partition@100 { + compatible = "zephyr,mapped-partition"; label = "code-partition"; reg = <0x100 (0x180000 - 0x100)>; read-only; }; nvm_partition: partition@180000 { + compatible = "zephyr,mapped-partition"; label = "nvm"; - reg = <0x180000 0x1000>; + reg = <0x180000 0x800>; + }; + + storage_partition: partition@180800 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x180800 0x800>; }; circuitpy_partition: partition@181000 { + compatible = "zephyr,mapped-partition"; label = "circuitpy"; reg = <0x181000 (DT_SIZE_M(2) - 0x181000)>; }; }; }; -#include "../app.overlay" +#include "../../../app.overlay" diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml index e596ea1aebc..4c396485d66 100644 --- a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = false zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/board.overlay b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/board.overlay new file mode 100644 index 00000000000..6a8343384c9 --- /dev/null +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/board.overlay @@ -0,0 +1,19 @@ +/* Remove slot1 (OTA), expand slot0 to use the space. + * CircuitPython doesn't use OTA updates. */ +&slot0_partition { + reg = <0x00010000 DT_SIZE_K(1024)>; +}; + +/delete-node/ &slot1_partition; + +&flash0 { + partitions{ + circuitpy_partition: partition@118000 { + compatible = "zephyr,mapped-partition"; + label = "circuitpy"; + reg = <0x118000 (DT_SIZE_M(4) - DT_SIZE_K(1120))>; + }; + }; +}; + +#include "../../../app.overlay" diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index 7751ec96715..d943b17bf26 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = false zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index 42bef11db0c..2291d43c4a1 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = true # Zephyr board has zephyr_display zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/board.conf b/ports/zephyr-cp/boards/renesas/ek_ra8d1/board.conf new file mode 100644 index 00000000000..451f4199116 --- /dev/null +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/board.conf @@ -0,0 +1,12 @@ + +# Enable Zephyr display subsystem so DT chosen zephyr,display creates a device. +CONFIG_DISPLAY=y + +# RA8 USB can generate bursts of UDC/USBD events (especially with HS + MSC). +# Keep queues/buffers larger to avoid dropped events that can desynchronize +# control-transfer state (e.g. "udc: Cannot determine the next stage"). +CONFIG_UDC_RENESAS_RA_MAX_QMESSAGES=32 +CONFIG_USBD_MAX_UDC_MSG=32 +CONFIG_USBD_MSG_SLAB_COUNT=32 +CONFIG_UDC_BUF_COUNT=32 +CONFIG_UDC_BUF_POOL_SIZE=2048 diff --git a/ports/zephyr-cp/boards/ek_ra8d1.overlay b/ports/zephyr-cp/boards/renesas/ek_ra8d1/board.overlay similarity index 59% rename from ports/zephyr-cp/boards/ek_ra8d1.overlay rename to ports/zephyr-cp/boards/renesas/ek_ra8d1/board.overlay index c13dfc457f5..416be7b510e 100644 --- a/ports/zephyr-cp/boards/ek_ra8d1.overlay +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/board.overlay @@ -2,4 +2,4 @@ /delete-node/ partitions; }; -#include "../app.overlay" +#include "../../../app.overlay" diff --git a/ports/zephyr-cp/boards/renesas_da14695_dk_usb.conf b/ports/zephyr-cp/boards/renesas_da14695_dk_usb.conf deleted file mode 100644 index 145a9393407..00000000000 --- a/ports/zephyr-cp/boards/renesas_da14695_dk_usb.conf +++ /dev/null @@ -1,20 +0,0 @@ -CONFIG_BT=y -CONFIG_BT_PERIPHERAL=y -CONFIG_BT_CENTRAL=y -CONFIG_BT_BROADCASTER=y -CONFIG_BT_OBSERVER=y -CONFIG_BT_EXT_ADV=y - -CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=y -CONFIG_BT_DEVICE_NAME_DYNAMIC=y -CONFIG_BT_DEVICE_NAME_MAX=28 -CONFIG_BT_L2CAP_TX_MTU=253 - -# BT Buffers -CONFIG_BT_BUF_CMD_TX_SIZE=255 -CONFIG_BT_BUF_EVT_RX_COUNT=16 -CONFIG_BT_BUF_EVT_RX_SIZE=255 -CONFIG_BT_BUF_ACL_TX_COUNT=3 -CONFIG_BT_BUF_ACL_TX_SIZE=251 -CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=1 -CONFIG_BT_BUF_ACL_RX_SIZE=255 diff --git a/ports/zephyr-cp/boards/silabs/siwx917_dk2605a/autogen_board_info.toml b/ports/zephyr-cp/boards/silabs/siwx917_dk2605a/autogen_board_info.toml new file mode 100644 index 00000000000..67701ccc2f6 --- /dev/null +++ b/ports/zephyr-cp/boards/silabs/siwx917_dk2605a/autogen_board_info.toml @@ -0,0 +1,129 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Silicon Labs SiWx917 Wi-Fi 6 and Bluetooth LE SoC Dev Kit (BRD2605A)" + +[modules] +__future__ = true +_bleio = true # Zephyr board has _bleio +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilewriter = false +audiofilters = false +audiofreeverb = false +audioi2sin = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = true +gifio = true # Zephyr board has busio +gnss = false +hashlib = true # Zephyr networking enabled +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = true # Zephyr networking enabled +is31fl3741 = false +jpegio = true # Zephyr board has busio +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = true # Zephyr networking enabled +spitarget = false +ssl = true # Zephyr networking enabled +storage = true +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_audio = false +usb_cdc = false +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = true # Zephyr board has wifi +zephyr_display = false +zephyr_kernel = false +zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/silabs/siwx917_dk2605a/board.conf b/ports/zephyr-cp/boards/silabs/siwx917_dk2605a/board.conf new file mode 100644 index 00000000000..0ad2a578ee8 --- /dev/null +++ b/ports/zephyr-cp/boards/silabs/siwx917_dk2605a/board.conf @@ -0,0 +1,93 @@ +# The upstream board defconfig does not enable GPIO, so no gpio device structs +# are instantiated and the generated board.c fails to link. +CONFIG_GPIO=y + +# getaddrinfo() calls k_calloc(), which is compiled out when the kernel heap is +# zero-sized. prj.conf also draws thread stacks from this heap. +CONFIG_HEAP_MEM_POOL_SIZE=16384 + +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_DHCPV4=y +CONFIG_NET_SOCKETS=y + +# Not implied by the options above, and the siwx91x driver defaults to the +# native stack. Without it association and DHCP still work, since DHCP is UDP, +# while every SOCK_STREAM socket fails. +CONFIG_NET_TCP=y + +# Every Zephyr socket also takes a zvfs file descriptor, which defaults to 0. +CONFIG_ZVFS_OPEN_MAX=8 + +# Poll slots for the socket-service thread, a separate budget from OPEN_MAX. +# The thread polls one entry beyond the number of registered services, so this +# must be strictly greater than that count. DNS_RESOLVER registers one service +# and MDNS_RESPONDER two, so the Zephyr default of 3 is one short and the +# service thread fails at boot, leaving every socket it owns unread. +CONFIG_ZVFS_POLL_MAX=6 + +# dns_resolve_reconfigure() is additive by default: it fills free slots and +# stops when there are none, then still returns 0. With one server slot held by +# DHCP, assigning to radio.dns would install nothing and report success. This +# makes reconfigure rebuild the list instead, which is the replace semantics +# the binding implies by exposing dns as a settable Sequence[str]. +CONFIG_DNS_RECONFIGURE_CLEANUP=y + +# Incoming connections are bounded by the net_conn array, not the TCP context +# slab. At the default of 8 the web workflow refuses connections during a burst +# because teardown cannot recycle slots fast enough, since each closed +# connection holds its slot for TIME_WAIT. 12 leaves enough headroom, and the +# context slab is raised alongside it so the slab is not the tighter bound. +CONFIG_NET_MAX_CONTEXTS=12 +CONFIG_NET_MAX_CONN=12 + +# Zephyr's console (printk, boot banner, driver logs) goes to SEGGER RTT over +# the existing SWD connection rather than sharing the UART with the REPL, where +# net event printks interleave with and corrupt the REPL stream. CircuitPython's +# REPL is unaffected: supervisor/serial.c takes its device from +# DT_CHOSEN(zephyr_console) and drives it directly. +CONFIG_USE_SEGGER_RTT=y +CONFIG_RTT_CONSOLE=y +CONFIG_UART_CONSOLE=n + +CONFIG_WIFI=y +CONFIG_NET_L2_WIFI_MGMT=y +CONFIG_NET_MGMT_EVENT=y +CONFIG_NET_MGMT_EVENT_INFO=y + +# Answers .local and advertises services registered through +# mdns.Server.advertise_service(). Zephyr's responder is push-only, so +# mdns.Server.find() has nothing to search with yet. +CONFIG_MDNS_RESPONDER=y +CONFIG_DNS_SD=y +CONFIG_MDNS_RESPONDER_DNS_SD=y + +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y +CONFIG_MBEDTLS_CIPHERSUITE_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256=y +CONFIG_MBEDTLS_ENTROPY_C=y +CONFIG_MBEDTLS_CTR_DRBG_C=y + +# Long writes need at least one prepare slot. Zephyr's unconditional default of +# 0 rejects any write that does not fit a single ATT_WRITE_REQ, and only an +# explicit assignment here overrides it. +CONFIG_BT_ATT_PREPARE_COUNT=2 + +# Zephyr's default preferred supervision timeout is 420ms, which the peripheral +# pushes onto the central shortly into every connection. That is thin on a +# single-antenna part whose network processor shares the radio with Wi-Fi. +# 400 = 4.0s, Apple's accessory guideline value. +CONFIG_BT_PERIPHERAL_PREF_MIN_INT=24 +CONFIG_BT_PERIPHERAL_PREF_MAX_INT=40 +CONFIG_BT_PERIPHERAL_PREF_LATENCY=0 +CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400 + +# The controller reports 15 ACL TX buffers while the host defaults to 3, which +# it warns about at boot. Match the host to the controller. +CONFIG_BT_BUF_ACL_TX_COUNT=15 + +# DHCP hands us a resolver, but the resolver subsystem has to be present for +# socketpool.getaddrinfo() and radio.ipv4_dns to work. +CONFIG_DNS_RESOLVER=y + +# DMA for the GSPI controller, which serves the breakout pads. +CONFIG_DMA=y diff --git a/ports/zephyr-cp/boards/silabs/siwx917_dk2605a/board.overlay b/ports/zephyr-cp/boards/silabs/siwx917_dk2605a/board.overlay new file mode 100644 index 00000000000..f345c30b9b0 --- /dev/null +++ b/ports/zephyr-cp/boards/silabs/siwx917_dk2605a/board.overlay @@ -0,0 +1,69 @@ +// Replaces the port's app.overlay, which does `&zephyr_udc0 { ... }` to add a +// CDC-ACM console. The SiWG917 has no USB device controller, so the console +// falls back to the Zephyr console on &ulpuart, reaching the host over the +// on-board J-Link VCOM. + +// CIRCUITPY filesystem. +// +// supervisor/flash.c needs a partition labeled `circuitpy_partition`, and the +// whole 8 MB is already partitioned by siwg917m111mgtba.dtsi with no +// unallocated space to claim. The upper region is owned by the network +// processor, which refuses M4 writes there, so CIRCUITPY is carved out of the +// M4's own code_partition, shrunk from 2008K to 1M. Overflowing that fails at +// link time rather than corrupting the filesystem. +&flash0 { + partitions { + code_partition: partition@202000 { + compatible = "zephyr,mapped-partition"; + label = "code_partition"; + reg = <0x00202000 0x00100000>; + }; + + circuitpy_partition: partition@302000 { + compatible = "zephyr,mapped-partition"; + label = "circuitpy"; + reg = <0x00302000 0x000f6000>; + }; + }; +}; + +// GSPI on the breakout pads. The board .dts leaves spi0 disabled and wires +// nothing, so the pin assignment here is taken from the mapping Silicon Labs +// ships for this board in +// zephyr/tests/drivers/spi/spi_loopback/boards/siwx917_dk2605a.overlay: +// CLK on pad 3, MOSI on pad 5, MISO on pad 7, CS on gpiob 12. +// +// spi0 is the SoC's only SPI controller node and is compatible "silabs,gspi". +// The on-board IMU is on a separate ULP SPI bus that has no controller node in +// devicetree, so it is not reachable through this one. +&pinctrl0 { + spi0_default: spi0_default { + out { + pinmux = , ; + }; + + in { + pinmux = ; + }; + }; +}; + +&spi0 { + status = "okay"; + cs-gpios = <&gpiob 12 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&spi0_default>; + pinctrl-names = "default"; + + dmas = <&gpdma 0 11>, <&gpdma 1 10>; + dma-names = "tx", "rx"; +}; + +&gpdma { + status = "okay"; +}; + +// 8 MB of PSRAM on the QSPI2 path. Present in ram_bounds; the Python heap does +// not use it yet. +&psram { + status = "okay"; +}; diff --git a/ports/zephyr-cp/boards/silabs/siwx917_dk2605a/circuitpython.toml b/ports/zephyr-cp/boards/silabs/siwx917_dk2605a/circuitpython.toml new file mode 100644 index 00000000000..1f15a6f8349 --- /dev/null +++ b/ports/zephyr-cp/boards/silabs/siwx917_dk2605a/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["rps"] diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml index 7ad9c4f2111..9ee6085d189 100644 --- a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = false zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/circuitpython.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/circuitpython.toml index 83e6bcd39c4..e8de92166db 100644 --- a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/circuitpython.toml +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/circuitpython.toml @@ -1 +1,5 @@ CIRCUITPY_BUILD_EXTENSIONS = ["hex"] + +# This part executes from RAM and is already at 93% of it, so it cannot +# absorb the roughly 90 KB ulab adds. +CIRCUITPY_ULAB = false diff --git a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml index 2796f76782f..59a15de0232 100644 --- a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = false zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml index 2d5385d90b2..3f83a46ec58 100644 --- a/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,6 +107,7 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = false usb_hid = false usb_host = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = true # Zephyr board has zephyr_display zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/stm32h750b_dk_stm32h750xx_ext_flash_app.conf b/ports/zephyr-cp/boards/st/stm32h750b_dk/board.conf similarity index 100% rename from ports/zephyr-cp/boards/stm32h750b_dk_stm32h750xx_ext_flash_app.conf rename to ports/zephyr-cp/boards/st/stm32h750b_dk/board.conf diff --git a/ports/zephyr-cp/boards/st/stm32h750b_dk/board.overlay b/ports/zephyr-cp/boards/st/stm32h750b_dk/board.overlay new file mode 100644 index 00000000000..7e038bb77aa --- /dev/null +++ b/ports/zephyr-cp/boards/st/stm32h750b_dk/board.overlay @@ -0,0 +1,21 @@ +&ext_flash { + partitions { + /delete-node/ partition@7800000; + + storage_partition: partition@7800000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x7800000 DT_SIZE_K(32)>; + }; + + circuitpy_partition: partition@7808000 { + compatible = "zephyr,mapped-partition"; + label = "circuitpy"; + reg = <0x7808000 (DT_SIZE_M(8) - DT_SIZE_K(32))>; + }; + }; +}; + +&rng { + status = "okay"; +}; diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml index 7f158d6f278..63fac014f17 100644 --- a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = true # Zephyr board has zephyr_display zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/stm32h7b3i_dk.conf b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/board.conf similarity index 100% rename from ports/zephyr-cp/boards/stm32h7b3i_dk.conf rename to ports/zephyr-cp/boards/st/stm32h7b3i_dk/board.conf diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/board.overlay b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/board.overlay new file mode 100644 index 00000000000..75e497714c9 --- /dev/null +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/board.overlay @@ -0,0 +1,45 @@ +&mx25lm51245 { + /delete-node/ partitions; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + storage_partition: partition@0 { + label = "storage"; + reg = <0x00000000 DT_SIZE_K(32)>; + }; + + circuitpy_partition: partition@8000 { + label = "circuitpy"; + reg = <0x00008000 (DT_SIZE_M(64) - DT_SIZE_K(32))>; + }; + }; +}; + +&sram5 { + status = "disabled"; +}; + +&rng { + status = "okay"; +}; + +&fdcan1 { + status = "disabled"; +}; + +/ { + chosen { + /delete-property/ zephyr,canbus; + }; +}; + +zephyr_udc0: &usbotg_hs { + pinctrl-0 = <&usb_otg_hs_dm_pa11 &usb_otg_hs_dp_pa12>; + pinctrl-names = "default"; + status = "okay"; +}; + +#include "../../../app.overlay" diff --git a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml index 19141f30065..10c7a39b60a 100644 --- a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml @@ -3,7 +3,7 @@ name = "STMicroelectronics STM32WBA65I Discovery kit" [modules] __future__ = true -_bleio = false +_bleio = true # Zephyr board has _bleio _eve = false _pew = false _pixelmap = false @@ -18,8 +18,10 @@ atexit = false audiobusio = false audiocore = false audiodelays = false +audiofilewriter = false audiofilters = false audiofreeverb = false +audioi2sin = false audioio = false audiomixer = false audiomp3 = false @@ -105,8 +107,9 @@ touchio = false traceback = true uheap = false usb = false +usb_audio = false usb_cdc = true -usb_hid = false +usb_hid = true usb_host = false usb_midi = false usb_video = false @@ -118,3 +121,9 @@ wifi = false zephyr_display = false zephyr_kernel = false zlib = true +# extmod modules shared with MicroPython +asyncio = true +binascii = true +json = true +re = true +select = true diff --git a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/board.conf b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/board.conf new file mode 100644 index 00000000000..d83be8dddb5 --- /dev/null +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/board.conf @@ -0,0 +1,10 @@ +# USB OTG on STM32WBA requires VOS Range 1. Keep HCLK > 16 MHz. +CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=32000000 + +# Bluetooth doesn't start up for some reason. +# CONFIG_BT=y +# CONFIG_BT_PERIPHERAL=y +# CONFIG_BT_CENTRAL=y + +# Board-specific BT calibration +CONFIG_BT_STM32WBA_USE_TEMP_BASED_CALIB=n diff --git a/ports/zephyr-cp/boards/stm32wba65i_dk1.overlay b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/board.overlay similarity index 67% rename from ports/zephyr-cp/boards/stm32wba65i_dk1.overlay rename to ports/zephyr-cp/boards/st/stm32wba65i_dk1/board.overlay index 3b72ffc9984..e1fc7f0175e 100644 --- a/ports/zephyr-cp/boards/stm32wba65i_dk1.overlay +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/board.overlay @@ -2,26 +2,29 @@ /delete-node/ partitions; partitions { - compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; boot_partition: partition@0 { + compatible = "zephyr,mapped-partition"; label = "mcuboot"; reg = <0x00000000 DT_SIZE_K(64)>; }; slot0_partition: partition@10000 { + compatible = "zephyr,mapped-partition"; label = "image-0"; reg = <0x00010000 DT_SIZE_K(928)>; }; storage_partition: partition@f80000 { + compatible = "zephyr,mapped-partition"; label = "storage"; reg = <0x001e0000 DT_SIZE_K(64)>; }; circuitpy_partition: partition@108000 { + compatible = "zephyr,mapped-partition"; label = "circuitpy"; reg = <0x00108000 DT_SIZE_K(992)>; }; @@ -58,4 +61,16 @@ zephyr_udc0: &usbotg_hs { status = "okay"; }; -#include "../app.overlay" +&bt_hci_wba { + /* Use HASH IRQ to allocate Radio SW Low Process (Warning HASH is not used) */ + interrupts = <66 0>, <61 14>; + interrupt-names = "radio", "radio-sw-low"; +}; + +&ieee802154 { + /* Use HASH IRQ to allocate Radio SW Low Process (Warning HASH is not used) */ + interrupts = <66 0>, <61 14>; + interrupt-names = "radio", "radio-sw-low"; +}; + +#include "../../../app.overlay" diff --git a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/circuitpython.toml b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/circuitpython.toml index 83e6bcd39c4..fb3dfea6867 100644 --- a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/circuitpython.toml +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/circuitpython.toml @@ -1 +1,2 @@ CIRCUITPY_BUILD_EXTENSIONS = ["hex"] +BLOBS=["hal_stm32"] diff --git a/ports/zephyr-cp/boards/stm32h750b_dk_stm32h750xx_ext_flash_app.overlay b/ports/zephyr-cp/boards/stm32h750b_dk_stm32h750xx_ext_flash_app.overlay deleted file mode 100644 index fdb4960477b..00000000000 --- a/ports/zephyr-cp/boards/stm32h750b_dk_stm32h750xx_ext_flash_app.overlay +++ /dev/null @@ -1,14 +0,0 @@ -&ext_flash { - partitions { - /delete-node/ partition@7800000; - - circuitpy_partition: partition@7800000 { - label = "circuitpy"; - reg = <0x7800000 DT_SIZE_M(8)>; - }; - }; -}; - -&rng { - status = "okay"; -}; diff --git a/ports/zephyr-cp/boards/stm32h7b3i_dk.overlay b/ports/zephyr-cp/boards/stm32h7b3i_dk.overlay deleted file mode 100644 index c2b5f3129c4..00000000000 --- a/ports/zephyr-cp/boards/stm32h7b3i_dk.overlay +++ /dev/null @@ -1,29 +0,0 @@ -&mx25lm51245 { - /delete-node/ partitions; -}; - -&sram5 { - status = "disabled"; -}; - -&rng { - status = "okay"; -}; - -&fdcan1 { - status = "disabled"; -}; - -/ { - chosen { - /delete-property/ zephyr,canbus; - }; -}; - -zephyr_udc0: &usbotg_hs { - pinctrl-0 = <&usb_otg_hs_dm_pa11 &usb_otg_hs_dp_pa12>; - pinctrl-names = "default"; - status = "okay"; -}; - -#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/stm32wba65i_dk1.conf b/ports/zephyr-cp/boards/stm32wba65i_dk1.conf deleted file mode 100644 index 55d951959e6..00000000000 --- a/ports/zephyr-cp/boards/stm32wba65i_dk1.conf +++ /dev/null @@ -1,24 +0,0 @@ -# USB OTG on STM32WBA requires VOS Range 1. Keep HCLK > 16 MHz. -CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=32000000 - -# Bluetooth doesn't start up for some reason. -# CONFIG_BT=y -# CONFIG_BT_PERIPHERAL=y -# CONFIG_BT_CENTRAL=y -CONFIG_BT_BROADCASTER=y -CONFIG_BT_OBSERVER=y -CONFIG_BT_EXT_ADV=y -CONFIG_BT_STM32WBA_USE_TEMP_BASED_CALIB=n - -CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=y -CONFIG_BT_DEVICE_NAME_DYNAMIC=y -CONFIG_BT_DEVICE_NAME_MAX=28 -CONFIG_BT_L2CAP_TX_MTU=253 - -# BT Buffers -CONFIG_BT_BUF_CMD_TX_SIZE=255 -CONFIG_BT_BUF_EVT_RX_COUNT=16 -CONFIG_BT_BUF_EVT_RX_SIZE=255 -CONFIG_BT_BUF_ACL_TX_SIZE=251 -CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=1 -CONFIG_BT_BUF_ACL_RX_SIZE=255 diff --git a/ports/zephyr-cp/common-hal/_bleio/Adapter.c b/ports/zephyr-cp/common-hal/_bleio/Adapter.c index d1410f02e1b..c3684a3b148 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Adapter.c +++ b/ports/zephyr-cp/common-hal/_bleio/Adapter.c @@ -12,25 +12,50 @@ #include #include #include +#include #include #include +#include #include "py/gc.h" #include "py/runtime.h" #include "bindings/zephyr_kernel/__init__.h" #include "shared-bindings/_bleio/__init__.h" +#include "common-hal/_bleio/__init__.h" #include "shared-bindings/_bleio/Adapter.h" #include "shared-bindings/_bleio/Address.h" #include "shared-module/_bleio/Address.h" #include "shared-module/_bleio/ScanResults.h" +#include "supervisor/background_callback.h" +#include "supervisor/shared/bluetooth/bluetooth.h" #include "supervisor/shared/tick.h" bleio_connection_internal_t bleio_connections[BLEIO_TOTAL_CONNECTION_COUNT]; +// Background pump: drains the BLE file-transfer / serial PacketBuffers by +// calling supervisor_bluetooth_background(). Queued from GATT write callbacks +// and connection events so the VM processes incoming data promptly. +static background_callback_t bluetooth_background_cb = {NULL, NULL}; + +static void bluetooth_adapter_background(void *data) { + (void)data; + supervisor_bluetooth_background(); +} + +void bleio_request_bluetooth_background(void) { + if (bluetooth_background_cb.fun != NULL) { + background_callback_add_core(&bluetooth_background_cb); + } +} + static bool scan_callbacks_registered = false; static bleio_scanresults_obj_t *active_scan_results = NULL; static struct bt_le_scan_cb scan_callbacks; static bool ble_advertising = false; +// True when advertising was started by the BLE workflow (supervisor) rather +// than user code. Lets the workflow restart its own adverts without disturbing +// user-initiated advertising. +static bool ble_advertising_internal = false; static bool ble_adapter_enabled = true; #define BLEIO_ADV_MAX_FIELDS 16 @@ -122,6 +147,8 @@ static void bleio_connection_clear(bleio_connection_internal_t *self) { } self->connection_obj = mp_const_none; + self->pair_status = PAIR_NOT_PAIRED; + self->sec_err = 0; } static void bleio_connection_release(bleio_connection_internal_t *connection, uint8_t reason) { @@ -139,33 +166,89 @@ static void bleio_connection_release(bleio_connection_internal_t *connection, ui common_hal_bleio_adapter_obj.connection_objs = NULL; } +// Per-connection ATT MTU exchange parameters. The params struct must persist +// until the exchange callback fires, so it lives for the lifetime of the slot. +static struct bt_gatt_exchange_params mtu_exchange_params[BLEIO_TOTAL_CONNECTION_COUNT]; + +static void on_mtu_exchanged(struct bt_conn *conn, uint8_t err, + struct bt_gatt_exchange_params *params) { + (void)conn; + (void)params; + if (err == 0) { + // Wake the workflow so outgoing_packet_length is recomputed with the + // now-larger negotiated MTU. + bleio_request_bluetooth_background(); + } +} + static void bleio_connected_cb(struct bt_conn *conn, uint8_t err) { if (err != 0) { return; } - if (bleio_connection_track(conn) == NULL) { + bleio_connection_internal_t *connection = bleio_connection_track(conn); + if (connection == NULL) { bt_conn_disconnect(conn, BT_HCI_ERR_CONN_LIMIT_EXCEEDED); return; } + // Initiate an ATT MTU exchange so the negotiated MTU reflects the larger + // payload our stack supports (CONFIG_BT_L2CAP_TX_MTU). Many centrals do + // this themselves, but if they don't we'd be stuck at the default 23-byte + // MTU (20-byte payload). That forces the file-transfer workflow to split + // protocol messages across notifications in ways peers can't reassemble + // (e.g. a listdir_entry whose second fragment begins with a 0x00 flags + // byte is misread as "unknown command 0x00"). bt_gatt_exchange_mtu returns + // -EALREADY if the peer already initiated, so this is safe either way. + size_t idx = (size_t)(connection - bleio_connections); + mtu_exchange_params[idx].func = on_mtu_exchanged; + int mtu_err = bt_gatt_exchange_mtu(conn, &mtu_exchange_params[idx]); + (void)mtu_err; + // When connectable advertising results in a connection, the controller // auto-stops advertising. Clear our flag to match (we cannot call // stop_advertising() here because this callback runs in Zephyr's BT // thread context). ble_advertising = false; + ble_advertising_internal = false; common_hal_bleio_adapter_obj.connection_objs = NULL; + + // Pump the workflow once now, and arm the recurring background callback + // so future GATT writes / events get drained by the VM. + bluetooth_background_cb.fun = bluetooth_adapter_background; + bluetooth_background_cb.data = NULL; + bluetooth_adapter_background(NULL); } static void bleio_disconnected_cb(struct bt_conn *conn, uint8_t reason) { - printk("disconnected %p\n", conn); + bleio_connection_discovery_abort(); bleio_connection_release(bleio_connection_find_by_conn(conn), reason); + bleio_request_bluetooth_background(); +} + +static void bleio_security_changed_cb(struct bt_conn *conn, bt_security_t level, + enum bt_security_err err) { + bleio_connection_internal_t *connection = bleio_connection_find_by_conn(conn); + if (connection == NULL) { + return; + } + + if (err == BT_SECURITY_ERR_SUCCESS && level > BT_SECURITY_L1) { + // Security was established (encryption enabled). + // This happens both on first-time pairing and when reconnecting + // with stored bond keys. + connection->pair_status = PAIR_PAIRED; + } else if (err != BT_SECURITY_ERR_SUCCESS) { + connection->pair_status = PAIR_NOT_PAIRED; + connection->sec_err = (uint8_t)err; + } } BT_CONN_CB_DEFINE(bleio_connection_callbacks) = { .connected = bleio_connected_cb, .disconnected = bleio_disconnected_cb, + .security_changed = bleio_security_changed_cb, }; static void scan_recv_cb(const struct bt_le_scan_recv_info *info, struct net_buf_simple *buf) { @@ -218,9 +301,8 @@ static size_t bleio_parse_adv_data(const uint8_t *raw, size_t raw_len, struct bt uint8_t data_len = field_len - 1; if (offset + field_len + 1 > raw_len || count >= out_len || - field_len < 1 || storage_offset + data_len > storage_len) { - mp_raise_ValueError(MP_ERROR_TEXT("Invalid advertising data")); + return 0; } uint8_t type = raw[offset + 1]; memcpy(storage + storage_offset, raw + offset + 2, data_len); @@ -258,10 +340,28 @@ void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enable } if (!bt_is_ready()) { int err = bt_enable(NULL); - if (err != 0) { + if (err != 0 && err != -EALREADY) { raise_zephyr_error(err); } + + // bt_init() returns early without setting BT_DEV_READY when + // CONFIG_BT_SETTINGS=y and no identity is loaded yet. + // Load settings so the BT settings handler fires and calls + // bt_finalize_init() which sets BT_DEV_READY. + settings_load(); + } + // Ensure a local identity exists so advertising/connections work and the + // name is stable across reboots. bt_id_create persists the identity when + // CONFIG_BT_SETTINGS is enabled. bleio_adapter_reset_name() reads the + // identity address back via common_hal_bleio_adapter_get_address(). + bt_addr_le_t id_addrs[CONFIG_BT_ID_MAX]; + size_t id_count = CONFIG_BT_ID_MAX; + bt_id_get(id_addrs, &id_count); + if (id_count == 0 || bt_addr_le_eq(&id_addrs[BT_ID_DEFAULT], BT_ADDR_LE_ANY)) { + (void)bt_id_create(NULL, NULL); + bt_id_get(id_addrs, &id_count); } + bleio_adapter_reset_name(self); ble_adapter_enabled = true; return; } @@ -302,13 +402,15 @@ mp_int_t common_hal_bleio_adapter_get_tx_power(bleio_adapter_obj_t *self) { return power; } -void common_hal_bleio_adapter_set_tx_power(bleio_adapter_obj_t *self, mp_int_t tx_power) { +// Non-raising variant of common_hal_bleio_adapter_set_tx_power for use from the +// BLE workflow, which runs outside the VM. Returns 0 on success. +static int bleio_adapter_set_tx_power_noraise(mp_int_t tx_power) { struct bt_hci_cp_vs_write_tx_power_level *cp; struct net_buf *buf, *rsp = NULL; buf = bt_hci_cmd_alloc(K_MSEC(3000)); if (!buf) { - mp_raise_msg(&mp_type_MemoryError, NULL); + return -ENOMEM; } cp = net_buf_add(buf, sizeof(*cp)); cp->handle_type = BT_HCI_VS_LL_HANDLE_TYPE_ADV; @@ -317,14 +419,40 @@ void common_hal_bleio_adapter_set_tx_power(bleio_adapter_obj_t *self, mp_int_t t int err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_WRITE_TX_POWER_LEVEL, buf, &rsp); if (err) { - raise_zephyr_error(err); + return err; } net_buf_unref(rsp); + return 0; +} + +void common_hal_bleio_adapter_set_tx_power(bleio_adapter_obj_t *self, mp_int_t tx_power) { + int err = bleio_adapter_set_tx_power_noraise(tx_power); + if (err) { + raise_zephyr_error(err); + } } bleio_address_obj_t *common_hal_bleio_adapter_get_address(bleio_adapter_obj_t *self) { - mp_raise_NotImplementedError(NULL); + // Report the local identity address, which is the same address used to + // derive the default adapter name in common_hal_bleio_adapter_set_enabled. + bt_addr_le_t id_addrs[CONFIG_BT_ID_MAX]; + size_t id_count = CONFIG_BT_ID_MAX; + bt_id_get(id_addrs, &id_count); + if (id_count == 0 || bt_addr_le_eq(&id_addrs[BT_ID_DEFAULT], BT_ADDR_LE_ANY)) { + (void)bt_id_create(NULL, NULL); + bt_id_get(id_addrs, &id_count); + } + + const bt_addr_le_t *addr = &id_addrs[BT_ID_DEFAULT]; + // Cache the address on the adapter so this can be called before the heap + // is available (e.g. from bleio_adapter_reset_name). The shared + // common_hal_bleio_address_construct() converts the raw address bytes + // into the Address object. + common_hal_bleio_address_construct(&self->address, addr->a.val, + bleio_address_type_from_zephyr(addr)); + self->address.base.type = &bleio_address_type; + return &self->address; } bool common_hal_bleio_adapter_set_address(bleio_adapter_obj_t *self, bleio_address_obj_t *address) { @@ -354,25 +482,31 @@ void common_hal_bleio_adapter_set_name(bleio_adapter_obj_t *self, const char *na } } -void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, - bool connectable, bool anonymous, uint32_t timeout, mp_float_t interval, - mp_buffer_info_t *advertising_data_bufinfo, - mp_buffer_info_t *scan_response_data_bufinfo, +// Internal start_advertising used by the BLE workflow (file transfer + serial +// services). Runs outside the VM, so it must not raise. Returns 0 on success or +// a positive errno on failure. A timeout of 0 means advertise indefinitely. +// This is the core implementation; common_hal_bleio_adapter_start_advertising() +// delegates here and translates errors into exceptions. +uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, + bool connectable, bool anonymous, uint32_t timeout, float interval, + const uint8_t *advertising_data, uint16_t advertising_data_len, + const uint8_t *scan_response_data, uint16_t scan_response_data_len, mp_int_t tx_power, const bleio_address_obj_t *directed_to) { (void)directed_to; (void)interval; + (void)anonymous; + (void)timeout; - if (advertising_data_bufinfo->len > BLEIO_ADV_MAX_DATA_LEN || - scan_response_data_bufinfo->len > BLEIO_ADV_MAX_DATA_LEN) { - mp_raise_NotImplementedError(NULL); - } - - if (timeout != 0) { - mp_raise_NotImplementedError(NULL); + if (advertising_data_len > BLEIO_ADV_MAX_DATA_LEN || + scan_response_data_len > BLEIO_ADV_MAX_DATA_LEN) { + return (uint32_t)EINVAL; } + // Don't disturb advertising that is already active (either user code or the + // workflow's own previous advert). The caller is responsible for stopping + // first if a restart is desired. if (ble_advertising) { - raise_zephyr_error(-EALREADY); + return (uint32_t)EBUSY; } bt_addr_le_t id_addrs[CONFIG_BT_ID_MAX]; @@ -381,30 +515,31 @@ void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, if (id_count == 0 || bt_addr_le_eq(&id_addrs[BT_ID_DEFAULT], BT_ADDR_LE_ANY)) { int id = bt_id_create(NULL, NULL); if (id < 0) { - printk("Failed to create identity address: %d\n", id); - raise_zephyr_error(id); + return (uint32_t)(-id); } } - size_t adv_count = bleio_parse_adv_data(advertising_data_bufinfo->buf, - advertising_data_bufinfo->len, + size_t adv_count = bleio_parse_adv_data(advertising_data, + advertising_data_len, adv_data, BLEIO_ADV_MAX_FIELDS, adv_data_storage, sizeof(adv_data_storage)); + if (adv_count == 0) { + return (uint32_t)EINVAL; + } size_t scan_resp_count = 0; - if (scan_response_data_bufinfo->len > 0) { - scan_resp_count = bleio_parse_adv_data(scan_response_data_bufinfo->buf, - scan_response_data_bufinfo->len, + if (scan_response_data_len > 0) { + scan_resp_count = bleio_parse_adv_data(scan_response_data, + scan_response_data_len, scan_resp_data, BLEIO_ADV_MAX_FIELDS, scan_resp_storage, sizeof(scan_resp_storage)); - } - - if (anonymous) { - mp_raise_NotImplementedError(NULL); + if (scan_resp_count == 0) { + return (uint32_t)EINVAL; + } } struct bt_le_adv_param adv_params; @@ -428,15 +563,75 @@ void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, NULL); } - common_hal_bleio_adapter_set_tx_power(self, tx_power); + // Best-effort TX power: the vendor HCI command may not exist on all + // controllers, so ignore failures here. + (void)bleio_adapter_set_tx_power_noraise(tx_power); - raise_zephyr_error(bt_le_adv_start(&adv_params, + int err = bt_le_adv_start(&adv_params, adv_data, adv_count, scan_resp_count > 0 ? scan_resp_data : NULL, - scan_resp_count)); + scan_resp_count); + if (err) { + return (uint32_t)(-err); + } ble_advertising = true; + // Default to workflow-owned; the public wrapper overrides this for user code. + ble_advertising_internal = true; + return 0; +} + +void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, + bool connectable, bool anonymous, uint32_t timeout, mp_float_t interval, + mp_buffer_info_t *advertising_data_bufinfo, + mp_buffer_info_t *scan_response_data_bufinfo, + mp_int_t tx_power, const bleio_address_obj_t *directed_to) { + (void)directed_to; + (void)interval; + + if (advertising_data_bufinfo->len > BLEIO_ADV_MAX_DATA_LEN || + scan_response_data_bufinfo->len > BLEIO_ADV_MAX_DATA_LEN) { + mp_raise_ValueError(MP_ERROR_TEXT("Data too large for advertisement packet")); + } + + if (timeout != 0) { + mp_raise_NotImplementedError(NULL); + } + + if (anonymous) { + mp_raise_NotImplementedError(NULL); + } + + if (ble_advertising) { + if (!ble_advertising_internal) { + // User code is already advertising. + raise_zephyr_error(-EALREADY); + } + // The workflow is advertising. Stop it so user code can take over. + common_hal_bleio_adapter_stop_advertising(self); + } + + uint32_t status = _common_hal_bleio_adapter_start_advertising(self, + connectable, + anonymous, + timeout, + interval, + advertising_data_bufinfo->buf, + advertising_data_bufinfo->len, + scan_response_data_bufinfo->buf, + scan_response_data_bufinfo->len, + tx_power, + directed_to); + if (status == (uint32_t)EINVAL) { + mp_raise_ValueError(MP_ERROR_TEXT("Invalid advertising data")); + } + if (status != 0) { + raise_zephyr_error(-(int)status); + } + + // Mark as user-owned so the workflow won't clobber it. + ble_advertising_internal = false; } void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self) { @@ -446,6 +641,7 @@ void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self) { } bt_le_adv_stop(); ble_advertising = false; + ble_advertising_internal = false; } bool common_hal_bleio_adapter_get_advertising(bleio_adapter_obj_t *self) { @@ -487,7 +683,11 @@ mp_obj_t common_hal_bleio_adapter_start_scan(bleio_adapter_obj_t *self, uint8_t struct bt_le_scan_param scan_params = { .type = active ? BT_LE_SCAN_TYPE_ACTIVE : BT_LE_SCAN_TYPE_PASSIVE, - .options = BT_LE_SCAN_OPT_FILTER_DUPLICATE, + /* Do not filter duplicates: the application merges advertisement and + * scan-response packets and needs to observe updated advertisements + * from the same device (e.g. when user code replaces the workflow + * advert). */ + .options = 0, .interval = interval_units, .window = window_units, .timeout = (uint16_t)timeout_units, @@ -564,13 +764,10 @@ mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_addre const uint16_t timeout_units = bleio_validate_and_convert_timeout(timeout); - mp_buffer_info_t address_bufinfo; - mp_get_buffer_raise(address->bytes, &address_bufinfo, MP_BUFFER_READ); - bt_addr_le_t peer = { .type = bleio_address_type_to_zephyr(address->type), }; - memcpy(peer.a.val, address_bufinfo.buf, NUM_BLEIO_ADDRESS_BYTES); + memcpy(peer.a.val, address->bytes, NUM_BLEIO_ADDRESS_BYTES); struct bt_conn_le_create_param create_params = BT_CONN_LE_CREATE_PARAM_INIT( BT_CONN_LE_OPT_NONE, @@ -622,11 +819,61 @@ mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_addre return bleio_connection_new_from_internal(connection); } +struct bond_collect_ctx { + bt_addr_le_t *addrs; + size_t *count; + size_t max; +}; + +static void bond_iterator_collect(const struct bt_bond_info *info, void *user_data) { + struct bond_collect_ctx *ctx = (struct bond_collect_ctx *)user_data; + if (*ctx->count < ctx->max) { + bt_addr_le_copy(&ctx->addrs[*ctx->count], &info->addr); + (*ctx->count)++; + } +} + +static void bond_iterator_check(const struct bt_bond_info *info, void *user_data) { + (void)info; + bool *has_bonds = (bool *)user_data; + *has_bonds = true; +} + void common_hal_bleio_adapter_erase_bonding(bleio_adapter_obj_t *self) { - mp_raise_NotImplementedError(NULL); + // Unpair all bonded devices for all local identities. + for (uint8_t id = 0; id < CONFIG_BT_ID_MAX; id++) { + // bt_unpair takes an addr; use bt_foreach_bond to iterate and unpair. + // We need to collect addresses first since we can't unpair during iteration. + bt_addr_le_t addrs[CONFIG_BT_MAX_PAIRED]; + size_t addr_count = 0; + + bt_foreach_bond(id, bond_iterator_collect, &(struct bond_collect_ctx) { + .addrs = addrs, + .count = &addr_count, + .max = CONFIG_BT_MAX_PAIRED + }); + + for (size_t i = 0; i < addr_count; i++) { + bt_unpair(id, &addrs[i]); + } + } + + // Reset pairing state on all connections + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connections[i].pair_status = PAIR_NOT_PAIRED; + bleio_connections[i].sec_err = 0; + } } bool common_hal_bleio_adapter_is_bonded_to_central(bleio_adapter_obj_t *self) { + // Check if any bond exists for identity 0 + for (uint8_t id = 0; id < CONFIG_BT_ID_MAX; id++) { + bool has_bonds = false; + bt_foreach_bond(id, bond_iterator_check, &has_bonds); + if (has_bonds) { + return true; + } + } return false; } @@ -661,6 +908,7 @@ void bleio_adapter_reset(bleio_adapter_obj_t *adapter) { adapter->connection_objs = NULL; active_scan_results = NULL; ble_advertising = false; + ble_advertising_internal = false; ble_adapter_enabled = bt_is_ready(); } diff --git a/ports/zephyr-cp/common-hal/_bleio/Adapter.h b/ports/zephyr-cp/common-hal/_bleio/Adapter.h index c15c698e2a5..25e1c35d563 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Adapter.h +++ b/ports/zephyr-cp/common-hal/_bleio/Adapter.h @@ -10,6 +10,7 @@ #include "py/obj.h" #include "py/objtuple.h" +#include "shared-bindings/_bleio/Address.h" #include "shared-bindings/_bleio/Connection.h" #include "shared-bindings/_bleio/ScanResults.h" @@ -23,7 +24,15 @@ typedef struct { mp_obj_t name; mp_obj_tuple_t *connection_objs; bool user_advertising; + // Cached local address returned by common_hal_bleio_adapter_get_address(). + // Stored inline so it never needs to allocate, even when read before the + // heap is available (e.g. from bleio_adapter_reset_name). + bleio_address_obj_t address; } bleio_adapter_obj_t; void bleio_adapter_gc_collect(bleio_adapter_obj_t *adapter); void bleio_adapter_reset(bleio_adapter_obj_t *adapter); + +// Queue a background run of supervisor_bluetooth_background() so the VM drains +// incoming BLE PacketBuffer data. Safe to call from Zephyr BT/workqueue context. +void bleio_request_bluetooth_background(void); diff --git a/ports/zephyr-cp/common-hal/_bleio/Characteristic.c b/ports/zephyr-cp/common-hal/_bleio/Characteristic.c index 386be6004d2..e1ba8b7349f 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Characteristic.c +++ b/ports/zephyr-cp/common-hal/_bleio/Characteristic.c @@ -5,16 +5,141 @@ // // SPDX-License-Identifier: MIT +#include + +#include +#include +#include + #include "py/runtime.h" +#include "py/gc.h" +#include "bindings/zephyr_kernel/__init__.h" +#include "shared-bindings/_bleio/__init__.h" #include "shared-bindings/_bleio/Characteristic.h" #include "shared-bindings/_bleio/Descriptor.h" #include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" +#include "common-hal/_bleio/__init__.h" +#include "common-hal/_bleio/CharacteristicBuffer.h" +#include "common-hal/_bleio/Connection.h" +#include "common-hal/_bleio/UUID.h" +#include "common-hal/_bleio/PacketBuffer.h" +#include "shared-bindings/_bleio/CharacteristicBuffer.h" +#include "shared-bindings/_bleio/PacketBuffer.h" +#include "supervisor/port_heap.h" + +// CCCD write callback — captures the subscribing connection for PacketBuffer. +ssize_t bleio_ccc_write_cb(struct bt_conn *conn, + const struct bt_gatt_attr *attr, uint16_t value) { + struct bt_gatt_ccc_managed_user_data *ccc_data = + (struct bt_gatt_ccc_managed_user_data *)attr->user_data; + bleio_characteristic_obj_t *characteristic = + (bleio_characteristic_obj_t *)((char *)ccc_data + - offsetof(bleio_characteristic_obj_t, zephyr_ccc)); + + if (characteristic->observer != mp_const_none && + mp_obj_is_type(characteristic->observer, &bleio_packet_buffer_type)) { + bleio_packet_buffer_set_conn( + MP_OBJ_TO_PTR(characteristic->observer), + value != 0 ? conn : NULL); + } + return sizeof(value); +} + +uint16_t bleio_security_to_zephyr_perm( + bleio_attribute_security_mode_t read_perm, + bleio_attribute_security_mode_t write_perm, + bleio_characteristic_properties_t props) { + uint16_t perm = 0; + + if (props & CHAR_PROP_READ) { + switch (read_perm) { + case SECURITY_MODE_OPEN: + perm |= BT_GATT_PERM_READ; + break; + case SECURITY_MODE_ENC_NO_MITM: + perm |= BT_GATT_PERM_READ_ENCRYPT; + break; + case SECURITY_MODE_ENC_WITH_MITM: + perm |= BT_GATT_PERM_READ_AUTHEN; + break; + case SECURITY_MODE_LESC_ENC_WITH_MITM: + perm |= BT_GATT_PERM_READ_LESC; + break; + default: + break; + } + } + + if (props & (CHAR_PROP_WRITE | CHAR_PROP_WRITE_NO_RESPONSE)) { + switch (write_perm) { + case SECURITY_MODE_OPEN: + perm |= BT_GATT_PERM_WRITE; + break; + case SECURITY_MODE_ENC_NO_MITM: + perm |= BT_GATT_PERM_WRITE_ENCRYPT; + break; + case SECURITY_MODE_ENC_WITH_MITM: + perm |= BT_GATT_PERM_WRITE_AUTHEN; + break; + case SECURITY_MODE_LESC_ENC_WITH_MITM: + perm |= BT_GATT_PERM_WRITE_LESC; + break; + default: + break; + } + } + + return perm; +} + +ssize_t bleio_char_read_cb(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset) { + bleio_characteristic_obj_t *self = attr->user_data; + (void)conn; + return bt_gatt_attr_read(conn, attr, buf, len, offset, + self->current_value, self->current_value_len); +} + +ssize_t bleio_char_write_cb(struct bt_conn *conn, + const struct bt_gatt_attr *attr, const void *buf, uint16_t len, + uint16_t offset, uint8_t flags) { + bleio_characteristic_obj_t *self = attr->user_data; + (void)flags; + if (offset + len > self->max_length) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + memcpy(self->current_value + offset, buf, len); + if (offset + len > self->current_value_len) { + self->current_value_len = offset + len; + } + + // Notify any observer (e.g., CharacteristicBuffer, PacketBuffer) of the write. + if (self->observer != mp_const_none) { + if (mp_obj_is_type(self->observer, &bleio_characteristic_buffer_type)) { + bleio_characteristic_buffer_extend(MP_OBJ_TO_PTR(self->observer), buf, len); + } else if (mp_obj_is_type(self->observer, &bleio_packet_buffer_type)) { + bleio_packet_buffer_extend(MP_OBJ_TO_PTR(self->observer), conn, buf, len); + } + } + + return len; +} + +void bleio_ccc_changed_cb(const struct bt_gatt_attr *attr, uint16_t value) { + // Track subscription state if needed in the future. + (void)attr; + (void)value; +} bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties(bleio_characteristic_obj_t *self) { return self->props; } mp_obj_tuple_t *common_hal_bleio_characteristic_get_descriptors(bleio_characteristic_obj_t *self) { + if (self->descriptor_list == NULL) { + return mp_const_empty_tuple; + } return mp_obj_new_tuple(self->descriptor_list->len, self->descriptor_list->items); } @@ -31,15 +156,103 @@ size_t common_hal_bleio_characteristic_get_max_length(bleio_characteristic_obj_t } size_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self, uint8_t *buf, size_t len) { - mp_raise_NotImplementedError(NULL); -} + if (self->service != NULL && self->service->is_remote) { + // Remote characteristic: read via GATT client + bleio_connection_obj_t *connection = MP_OBJ_TO_PTR(self->service->connection); + if (connection == NULL || connection->connection == NULL || + connection->connection->conn == NULL) { + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Not connected")); + } + + return bleio_gattc_read_sync(connection->connection->conn, + self->handle, buf, len); + } -void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self, bleio_descriptor_obj_t *descriptor) { - mp_raise_NotImplementedError(NULL); + // Local characteristic + size_t copy_len = self->current_value_len; + if (copy_len > len) { + copy_len = len; + } + memcpy(buf, self->current_value, copy_len); + return copy_len; } -void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, uint16_t handle, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo, const char *user_description) { - mp_raise_NotImplementedError(NULL); +void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, + bleio_service_obj_t *service, uint16_t handle, bleio_uuid_obj_t *uuid, + bleio_characteristic_properties_t props, + bleio_attribute_security_mode_t read_perm, + bleio_attribute_security_mode_t write_perm, + mp_int_t max_length, bool fixed_length, + mp_buffer_info_t *initial_value_bufinfo, + const char *user_description) { + + self->service = service; + self->uuid = uuid; + self->handle = handle; + self->props = props; + self->read_perm = read_perm; + self->write_perm = write_perm; + self->max_length = max_length; + self->fixed_length = fixed_length; + self->observer = mp_const_none; + // The descriptor list is an mp_obj (GC object). When constructed before + // gc_init() (e.g. the BLE workflow at boot), the GC heap isn't available, + // so leave it NULL and lazily create it on first use. Matches the nordic + // port's handling. + if (gc_alloc_possible()) { + self->descriptor_list = mp_obj_new_list(0, NULL); + } else { + self->descriptor_list = NULL; + } + + // Allocate value buffer + self->current_value = port_malloc(max_length, false); + memset(self->current_value, 0, max_length); + self->current_value_alloc = max_length; + self->current_value_len = 0; + + // Copy initial value if provided + if (initial_value_bufinfo != NULL && initial_value_bufinfo->len > 0) { + size_t len = initial_value_bufinfo->len; + if (len > (size_t)max_length) { + len = max_length; + } + memcpy(self->current_value, initial_value_bufinfo->buf, len); + self->current_value_len = len; + } + + // Convert UUID to Zephyr format + bleio_uuid_to_zephyr(uuid, &self->zephyr_uuid); + + if (service->is_remote) { + // Remote characteristic: just add to the service's list + mp_obj_list_append(MP_OBJ_FROM_PTR(service->characteristic_list), + MP_OBJ_FROM_PTR(self)); + } else { + common_hal_bleio_service_add_characteristic(service, self, + initial_value_bufinfo, user_description); + + // Create a Descriptor object for user_description (CUD 0x2901) + if (user_description != NULL && user_description[0] != '\0') { + bleio_uuid_obj_t *desc_uuid = mp_obj_malloc(bleio_uuid_obj_t, &bleio_uuid_type); + common_hal_bleio_uuid_construct(desc_uuid, 0x2901, NULL); + + bleio_descriptor_obj_t *descriptor = mp_obj_malloc(bleio_descriptor_obj_t, &bleio_descriptor_type); + + size_t desc_len = strlen(user_description); + mp_buffer_info_t desc_bufinfo = { + .buf = (void *)user_description, + .len = desc_len, + }; + + common_hal_bleio_descriptor_construct( + descriptor, self, desc_uuid, + SECURITY_MODE_OPEN, SECURITY_MODE_OPEN, + desc_len, false, &desc_bufinfo); + + common_hal_bleio_characteristic_add_descriptor(self, descriptor); + } + } } bool common_hal_bleio_characteristic_deinited(bleio_characteristic_obj_t *self) { @@ -47,15 +260,148 @@ bool common_hal_bleio_characteristic_deinited(bleio_characteristic_obj_t *self) } void common_hal_bleio_characteristic_deinit(bleio_characteristic_obj_t *self) { - // Nothing to do + // Nothing to do - service handles unregistration + if (self->current_value != NULL) { + port_free(self->current_value); + self->current_value = NULL; + self->current_value_alloc = 0; + self->current_value_len = 0; + } +} + +// Struct for tracking GATT notification subscriptions on remote characteristics. +typedef struct { + struct bt_gatt_subscribe_params params; + uint16_t value_handle; + uint16_t ccc_handle; + bleio_characteristic_obj_t *characteristic; + volatile bool subscribed; +} zephyr_subscription_t; + +static uint8_t on_gattc_notify(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params, + const void *data, uint16_t length) { + zephyr_subscription_t *sub = CONTAINER_OF(params, zephyr_subscription_t, params); + if (sub->characteristic != NULL && + sub->characteristic->observer != mp_const_none) { + if (mp_obj_is_type(sub->characteristic->observer, &bleio_characteristic_buffer_type)) { + bleio_characteristic_buffer_extend( + MP_OBJ_TO_PTR(sub->characteristic->observer), data, length); + } else if (mp_obj_is_type(sub->characteristic->observer, &bleio_packet_buffer_type)) { + bleio_packet_buffer_extend( + MP_OBJ_TO_PTR(sub->characteristic->observer), conn, data, length); + } + } + return BT_GATT_ITER_CONTINUE; } void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate) { - mp_raise_NotImplementedError(NULL); + // Only valid for remote characteristics (client-side). + if (self->service == NULL || !self->service->is_remote) { + return; + } + + bleio_connection_obj_t *connection_obj = MP_OBJ_TO_PTR(self->service->connection); + if (connection_obj == NULL || connection_obj->connection == NULL || + connection_obj->connection->conn == NULL) { + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Not connected")); + } + + struct bt_conn *conn = connection_obj->connection->conn; + + if (!notify && !indicate) { + // Unsubscribe from any existing subscription. + // We don't track subscriptions per-characteristic yet, + // so just return for now. + return; + } + + if (self->cccd_handle == 0) { + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("No CCCD for this Characteristic")); + } + + // Allocate subscription tracking from port heap (won't move on GC). + // Note: Simplified - we allocate a new subscription each time. + // A production implementation would track and reuse subscriptions. + zephyr_subscription_t *sub = port_malloc(sizeof(zephyr_subscription_t), false); + if (sub == NULL) { + mp_raise_msg(&mp_type_MemoryError, NULL); + } + + // Zero-initialize the entire structure so that unset fields + // (subscribe, flags, discover_params, work_q, node) are NULL/0 + // rather than garbage from the heap. bt_gatt_subscribe checks + // subscribe==NULL to decide whether to do a synchronous write. + memset(sub, 0, sizeof(zephyr_subscription_t)); + + sub->characteristic = self; + sub->value_handle = self->handle; + sub->ccc_handle = self->cccd_handle; + sub->subscribed = false; + + sub->params.notify = on_gattc_notify; + sub->params.value_handle = self->handle; + sub->params.ccc_handle = self->cccd_handle; + sub->params.value = notify ? BT_GATT_CCC_NOTIFY : BT_GATT_CCC_INDICATE; + + int err = bt_gatt_subscribe(conn, &sub->params); + if (err != 0) { + port_free(sub); + raise_zephyr_error(err); + } + sub->subscribed = true; } void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) { - mp_raise_NotImplementedError(NULL); + if (self->service != NULL && self->service->is_remote) { + // Remote characteristic: write via GATT client + bleio_connection_obj_t *connection = MP_OBJ_TO_PTR(self->service->connection); + if (connection == NULL || connection->connection == NULL || + connection->connection->conn == NULL) { + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Not connected")); + } + + if (self->props & CHAR_PROP_WRITE_NO_RESPONSE) { + int err = bt_gatt_write_without_response( + connection->connection->conn, + self->handle, + bufinfo->buf, bufinfo->len, false); + if (err != 0) { + raise_zephyr_error(err); + } + } else { + bleio_gattc_write_sync(connection->connection->conn, + self->handle, bufinfo->buf, bufinfo->len); + } + return; + } + + // Local characteristic + size_t len = bufinfo->len; + if (len > self->max_length) { + len = self->max_length; + } + memcpy(self->current_value, bufinfo->buf, len); + self->current_value_len = len; + + // If NOTIFY and service is registered, send notification + if ((self->props & CHAR_PROP_NOTIFY) && self->service != NULL && + self->service->registered) { + bt_gatt_notify(NULL, &self->service->attrs[self->value_attr_index], + self->current_value, self->current_value_len); + } +} + +void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self, + bleio_descriptor_obj_t *descriptor) { + if (self->descriptor_list == NULL) { + self->descriptor_list = mp_obj_new_list(0, NULL); + } + mp_obj_list_append(MP_OBJ_FROM_PTR(self->descriptor_list), + MP_OBJ_FROM_PTR(descriptor)); + // Descriptors added after characteristic construction would need + // service re-registration; for now the common case is handled by + // Service.add_characteristic which adds descriptors at registration time. } void bleio_characteristic_set_observer(bleio_characteristic_obj_t *self, mp_obj_t observer) { diff --git a/ports/zephyr-cp/common-hal/_bleio/Characteristic.h b/ports/zephyr-cp/common-hal/_bleio/Characteristic.h index b710a9f2662..f4a578c888a 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Characteristic.h +++ b/ports/zephyr-cp/common-hal/_bleio/Characteristic.h @@ -7,6 +7,8 @@ #pragma once +#include + #include "py/obj.h" #include "py/objlist.h" #include "shared-bindings/_bleio/Attribute.h" @@ -34,7 +36,28 @@ typedef struct _bleio_characteristic_obj { uint16_t cccd_handle; uint16_t sccd_handle; bool fixed_length; + // Zephyr GATT server fields: + struct bt_gatt_chrc zephyr_chrc; + struct bt_uuid_128 zephyr_uuid; + struct bt_gatt_ccc_managed_user_data zephyr_ccc; + size_t value_attr_index; // index in service attrs for notify } bleio_characteristic_obj_t; void bleio_characteristic_set_observer(bleio_characteristic_obj_t *self, mp_obj_t observer); void bleio_characteristic_clear_observer(bleio_characteristic_obj_t *self); + +// Callbacks used by Service.c when building GATT attr table +ssize_t bleio_char_read_cb(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset); +ssize_t bleio_char_write_cb(struct bt_conn *conn, + const struct bt_gatt_attr *attr, const void *buf, uint16_t len, + uint16_t offset, uint8_t flags); +void bleio_ccc_changed_cb(const struct bt_gatt_attr *attr, uint16_t value); +ssize_t bleio_ccc_write_cb(struct bt_conn *conn, + const struct bt_gatt_attr *attr, uint16_t value); + +// Permission mapping helper +uint16_t bleio_security_to_zephyr_perm( + bleio_attribute_security_mode_t read_perm, + bleio_attribute_security_mode_t write_perm, + bleio_characteristic_properties_t props); diff --git a/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.c b/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.c index 17e000e905e..9ce48ac97c4 100644 --- a/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.c +++ b/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.c @@ -5,69 +5,132 @@ // // SPDX-License-Identifier: MIT -#include "py/mperrno.h" +#include +#include + +#include + #include "py/runtime.h" +#include "py/stream.h" + +#include "shared/runtime/interrupt_char.h" + +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Connection.h" #include "shared-bindings/_bleio/CharacteristicBuffer.h" +#include "supervisor/shared/tick.h" + +#include "common-hal/_bleio/CharacteristicBuffer.h" +#include "common-hal/_bleio/Characteristic.h" + +// Zephyr's ring_buf is safe for single-producer/single-consumer without +// locks. The GATT callbacks (system workqueue) are the sole producer; +// the CircuitPython VM (main thread) is the sole consumer. + +// Called from Zephyr GATT callbacks (system workqueue context). +void bleio_characteristic_buffer_extend(bleio_characteristic_buffer_obj_t *self, + const uint8_t *data, size_t len) { + if (self->watch_for_interrupt_char) { + for (size_t i = 0; i < len; i++) { + if (data[i] == mp_interrupt_char) { + mp_sched_keyboard_interrupt(); + ring_buf_reset(&self->ringbuf); + } else { + ring_buf_put(&self->ringbuf, &data[i], 1); + } + } + } else { + ring_buf_put(&self->ringbuf, data, len); + } +} + void _common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, mp_float_t timeout, uint8_t *buffer, size_t buffer_size, void *static_handler_entry, bool watch_for_interrupt_char) { - (void)self; - (void)characteristic; - (void)timeout; - (void)buffer; - (void)buffer_size; - (void)static_handler_entry; - (void)watch_for_interrupt_char; - mp_raise_NotImplementedError(NULL); + + self->characteristic = characteristic; + self->timeout_ms = timeout * 1000; + self->watch_for_interrupt_char = watch_for_interrupt_char; + self->ringbuf_data = buffer; + ring_buf_init(&self->ringbuf, buffer_size, buffer); + + // Set ourselves as the characteristic's observer so we receive + // incoming writes (local) and notifications (remote). + bleio_characteristic_set_observer(characteristic, MP_OBJ_FROM_PTR(self)); } +// Assumes that timeout and buffer_size have been validated before call. void common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, mp_float_t timeout, size_t buffer_size) { - (void)self; - (void)characteristic; - (void)timeout; - (void)buffer_size; - mp_raise_NotImplementedError(NULL); + uint8_t *buffer = m_malloc_without_collect(buffer_size); + _common_hal_bleio_characteristic_buffer_construct(self, characteristic, timeout, + buffer, buffer_size, NULL, false); } -uint32_t common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self, uint8_t *data, size_t len, int *errcode) { - (void)self; - (void)data; - (void)len; - if (errcode != NULL) { - *errcode = MP_EAGAIN; +uint32_t common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self, + uint8_t *data, size_t len, int *errcode) { + uint64_t start_ticks = supervisor_ticks_ms64(); + + // Wait for all bytes received or timeout + while ((ring_buf_size_get(&self->ringbuf) < len) && + (supervisor_ticks_ms64() - start_ticks < self->timeout_ms)) { + RUN_BACKGROUND_TASKS; + // Allow user to break out of a timeout with a KeyboardInterrupt. + if (mp_hal_is_interrupted()) { + return 0; + } } - mp_raise_NotImplementedError(NULL); + + return ring_buf_get(&self->ringbuf, data, len); } -uint32_t common_hal_bleio_characteristic_buffer_rx_characters_available(bleio_characteristic_buffer_obj_t *self) { - (void)self; - mp_raise_NotImplementedError(NULL); +uint32_t common_hal_bleio_characteristic_buffer_rx_characters_available( + bleio_characteristic_buffer_obj_t *self) { + return ring_buf_size_get(&self->ringbuf); } -void common_hal_bleio_characteristic_buffer_clear_rx_buffer(bleio_characteristic_buffer_obj_t *self) { - (void)self; - mp_raise_NotImplementedError(NULL); +void common_hal_bleio_characteristic_buffer_clear_rx_buffer( + bleio_characteristic_buffer_obj_t *self) { + ring_buf_reset(&self->ringbuf); } bool common_hal_bleio_characteristic_buffer_deinited(bleio_characteristic_buffer_obj_t *self) { - return self->deinited; + return self->characteristic == NULL; } void common_hal_bleio_characteristic_buffer_deinit(bleio_characteristic_buffer_obj_t *self) { - if (self == NULL) { + if (common_hal_bleio_characteristic_buffer_deinited(self)) { return; } - self->deinited = true; + bleio_characteristic_clear_observer(self->characteristic); + self->characteristic = NULL; + // ringbuf_data was allocated with m_malloc_without_collect; free it. + m_free(self->ringbuf_data); + self->ringbuf_data = NULL; } bool common_hal_bleio_characteristic_buffer_connected(bleio_characteristic_buffer_obj_t *self) { - (void)self; - return false; + bleio_characteristic_obj_t *characteristic = self->characteristic; + if (characteristic == NULL || characteristic->service == NULL) { + return false; + } + + if (!characteristic->service->is_remote) { + // Local service: we're always "connected" as long as the service exists. + return true; + } + + // Remote service: check if the connection is still active. + if (characteristic->service->connection == mp_const_none) { + return false; + } + bleio_connection_obj_t *connection = + MP_OBJ_TO_PTR(characteristic->service->connection); + return common_hal_bleio_connection_get_connected(connection); } diff --git a/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.h b/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.h index 91ea262945a..85a5d253be2 100644 --- a/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.h +++ b/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.h @@ -9,11 +9,21 @@ #include +#include + #include "py/obj.h" #include "shared-bindings/_bleio/Characteristic.h" typedef struct { mp_obj_base_t base; bleio_characteristic_obj_t *characteristic; - bool deinited; + uint32_t timeout_ms; + struct ring_buf ringbuf; + uint8_t *ringbuf_data; + bool watch_for_interrupt_char; } bleio_characteristic_buffer_obj_t; + +// Called from GATT callbacks (system workqueue context) to push +// data into the CharacteristicBuffer ring buffer. +void bleio_characteristic_buffer_extend(bleio_characteristic_buffer_obj_t *self, + const uint8_t *data, size_t len); diff --git a/ports/zephyr-cp/common-hal/_bleio/Connection.c b/ports/zephyr-cp/common-hal/_bleio/Connection.c index 938359c79ca..77942728a5f 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Connection.c +++ b/ports/zephyr-cp/common-hal/_bleio/Connection.c @@ -6,17 +6,524 @@ // SPDX-License-Identifier: MIT #include +#include #include #include +#include +#include #include "py/runtime.h" +#include "shared/runtime/interrupt_char.h" #include "bindings/zephyr_kernel/__init__.h" #include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Characteristic.h" #include "shared-bindings/_bleio/Connection.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" +#include "common-hal/_bleio/__init__.h" +#include "common-hal/_bleio/Characteristic.h" +#include "supervisor/port_heap.h" + +// Discovery context passed through Zephyr callbacks. +typedef struct { + volatile bool done; + volatile int err; +} discovery_context_t; + +// Convert a Zephyr bt_uuid to a CircuitPython bleio UUID object. +// Must be called OUTSIDE Zephyr callback context. +static bleio_uuid_obj_t *bleio_uuid_from_zephyr(const struct bt_uuid *zuuid) { + bleio_uuid_obj_t *uuid = mp_obj_malloc(bleio_uuid_obj_t, &bleio_uuid_type); + if (zuuid->type == BT_UUID_TYPE_16) { + common_hal_bleio_uuid_construct(uuid, BT_UUID_16(zuuid)->val, NULL); + } else { + // Both Zephyr and CP store UUID bytes in little-endian order. + const struct bt_uuid_128 *uuid128 = BT_UUID_128(zuuid); + common_hal_bleio_uuid_construct(uuid, + (uuid128->val[13] << 8) | uuid128->val[12], uuid128->val); + } + return uuid; +} + +// Temporary storage for discovered services before creating CP objects. +// Each node is port_malloc'd in the callback and appended to a sys_slist. +typedef struct { + sys_snode_t node; + struct bt_uuid_128 uuid; + uint16_t start_handle; + uint16_t end_handle; +} discovered_service_t; + +// Temporary storage for discovered characteristics before creating CP objects. +typedef struct { + sys_snode_t node; + struct bt_uuid_128 uuid; + uint16_t value_handle; + uint16_t decl_handle; + uint8_t properties; +} discovered_char_t; + +// Temporary storage for discovered descriptors. +typedef struct { + sys_snode_t node; + struct bt_uuid_128 uuid; + uint16_t handle; +} discovered_desc_t; + +// File-scope discovery state for synchronous blocking. +static discovery_context_t *active_discovery_ctx; +static sys_slist_t discovered_list; +static struct bt_gatt_discover_params discovery_params; + +// Called from the disconnect callback (BT workqueue context) to abort any +// in-flight GATT discovery. Without this the main thread's spin loop would +// hang waiting for a callback that will never come, and the caller would then +// NULL-dereference the now-cleared connection in bt_gatt_discover(). +void bleio_connection_discovery_abort(void) { + discovery_context_t *ctx = active_discovery_ctx; + if (ctx != NULL) { + ctx->err = -ENOTCONN; + ctx->done = true; + } +} + +static uint8_t on_service_discovered(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params) { + discovery_context_t *ctx = active_discovery_ctx; + + if (attr == NULL) { + ctx->done = true; + return BT_GATT_ITER_STOP; + } + + discovered_service_t *ds = port_malloc(sizeof(discovered_service_t), false); + if (ds == NULL) { + ctx->done = true; + return BT_GATT_ITER_STOP; + } + + struct bt_gatt_service_val *svc_val = (struct bt_gatt_service_val *)attr->user_data; + ds->start_handle = attr->handle; + ds->end_handle = svc_val->end_handle; + + // Copy UUID into our storage + if (svc_val->uuid->type == BT_UUID_TYPE_16) { + ds->uuid.uuid.type = BT_UUID_TYPE_16; + ((struct bt_uuid_16 *)&ds->uuid)->val = BT_UUID_16(svc_val->uuid)->val; + } else { + memcpy(&ds->uuid, svc_val->uuid, sizeof(struct bt_uuid_128)); + } + + sys_slist_append(&discovered_list, &ds->node); + + return BT_GATT_ITER_CONTINUE; +} + +static uint8_t on_characteristic_discovered(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params) { + discovery_context_t *ctx = active_discovery_ctx; + + if (attr == NULL) { + ctx->done = true; + return BT_GATT_ITER_STOP; + } + + discovered_char_t *dc = port_malloc(sizeof(discovered_char_t), false); + if (dc == NULL) { + ctx->done = true; + return BT_GATT_ITER_STOP; + } + + struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data; + dc->value_handle = chrc->value_handle; + dc->decl_handle = attr->handle; + dc->properties = chrc->properties; + + // Copy UUID into our storage + if (chrc->uuid->type == BT_UUID_TYPE_16) { + dc->uuid.uuid.type = BT_UUID_TYPE_16; + ((struct bt_uuid_16 *)&dc->uuid)->val = BT_UUID_16(chrc->uuid)->val; + } else { + memcpy(&dc->uuid, chrc->uuid, sizeof(struct bt_uuid_128)); + } + + sys_slist_append(&discovered_list, &dc->node); + + return BT_GATT_ITER_CONTINUE; +} + +// Pairing: characteristic pointer with its declaration handle for descriptor discovery. +typedef struct { + bleio_characteristic_obj_t *characteristic; + uint16_t decl_handle; +} char_with_decl_t; + +// Forward declaration for use by descriptor discovery. +static void free_discovered_list(void); +static void bleio_discovery_check_connected(struct bt_conn *conn, + discovery_context_t *ctx); + +// Callback for descriptor discovery. +static uint8_t on_descriptor_discovered(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params) { + discovery_context_t *ctx = active_discovery_ctx; + + if (attr == NULL) { + ctx->done = true; + return BT_GATT_ITER_STOP; + } + + discovered_desc_t *dd = port_malloc(sizeof(discovered_desc_t), false); + if (dd == NULL) { + ctx->done = true; + return BT_GATT_ITER_STOP; + } + + dd->handle = attr->handle; + + // Copy UUID into our storage + if (attr->uuid->type == BT_UUID_TYPE_16) { + dd->uuid.uuid.type = BT_UUID_TYPE_16; + ((struct bt_uuid_16 *)&dd->uuid)->val = BT_UUID_16(attr->uuid)->val; + } else { + memcpy(&dd->uuid, attr->uuid, sizeof(struct bt_uuid_128)); + } + + sys_slist_append(&discovered_list, &dd->node); + + return BT_GATT_ITER_CONTINUE; +} + +// Create descriptors from discovered_list and add them to the characteristic. +// Called OUTSIDE the Zephyr callback context. Drains and frees all nodes. +static void create_descriptors_from_discovered(bleio_characteristic_obj_t *characteristic) { + sys_snode_t *node; + while ((node = sys_slist_get(&discovered_list)) != NULL) { + discovered_desc_t *dd = CONTAINER_OF(node, discovered_desc_t, node); + + bleio_uuid_obj_t *uuid = bleio_uuid_from_zephyr(&dd->uuid.uuid); + + // Remember handles for certain well-known descriptors. + if (dd->uuid.uuid.type == BT_UUID_TYPE_16) { + switch (((struct bt_uuid_16 *)&dd->uuid)->val) { + case 0x2902: + characteristic->cccd_handle = dd->handle; + break; + case 0x2903: + characteristic->sccd_handle = dd->handle; + break; + case 0x2901: + characteristic->user_desc_handle = dd->handle; + break; + default: + break; + } + } + + bleio_descriptor_obj_t *descriptor = + mp_obj_malloc(bleio_descriptor_obj_t, &bleio_descriptor_type); + + // Remote descriptors: set characteristic and UUID only. + // Reads/writes go over GATT via the handle. + descriptor->characteristic = characteristic; + descriptor->uuid = uuid; + descriptor->handle = dd->handle; + descriptor->read_perm = SECURITY_MODE_OPEN; + descriptor->write_perm = SECURITY_MODE_OPEN; + descriptor->max_length = 20; + descriptor->fixed_length = false; + descriptor->value = m_malloc(20); + memset(descriptor->value, 0, 20); + descriptor->value_length = 0; + + common_hal_bleio_characteristic_add_descriptor(characteristic, descriptor); + + port_free(dd); + } +} + +// Discover descriptors for a single characteristic. +static void discover_descriptors_for_characteristic(struct bt_conn *conn, + discovery_context_t *ctx, bleio_characteristic_obj_t *characteristic, + uint16_t end_handle) { + // Bail cleanly if the link dropped before this phase began. + bleio_discovery_check_connected(conn, ctx); + uint16_t start = characteristic->handle + 1; + if (start > end_handle) { + return; + } + + sys_slist_init(&discovered_list); + ctx->done = false; + ctx->err = 0; + + memset(&discovery_params, 0, sizeof(discovery_params)); + discovery_params.uuid = NULL; + discovery_params.start_handle = start; + discovery_params.end_handle = end_handle; + discovery_params.type = BT_GATT_DISCOVER_DESCRIPTOR; + discovery_params.func = on_descriptor_discovered; + + int err = bt_gatt_discover(conn, &discovery_params); + if (err != 0) { + free_discovered_list(); + if (err == -ENOENT) { + return; + } + raise_zephyr_error(err); + } + + while (!ctx->done) { + RUN_BACKGROUND_TASKS; + } + + // The link may have dropped while we were waiting. + bleio_discovery_check_connected(conn, ctx); + + create_descriptors_from_discovered(characteristic); +} + +// Create CircuitPython characteristic objects from the discovered_list. +// Called OUTSIDE the Zephyr callback context so MP allocations are safe. +// Drains and frees all nodes from the list. +// Returns the number of characteristics created and fills the chars_out array +// (which must be large enough). +static size_t create_characteristics_from_discovered(bleio_service_obj_t *service, + char_with_decl_t *chars_out, size_t max_chars) { + size_t count = 0; + sys_snode_t *node; + while ((node = sys_slist_get(&discovered_list)) != NULL) { + discovered_char_t *dc = CONTAINER_OF(node, discovered_char_t, node); + + bleio_uuid_obj_t *uuid = bleio_uuid_from_zephyr(&dc->uuid.uuid); + + bleio_characteristic_properties_t props = 0; + if (dc->properties & BT_GATT_CHRC_BROADCAST) { + props |= CHAR_PROP_BROADCAST; + } + if (dc->properties & BT_GATT_CHRC_READ) { + props |= CHAR_PROP_READ; + } + if (dc->properties & BT_GATT_CHRC_WRITE_WITHOUT_RESP) { + props |= CHAR_PROP_WRITE_NO_RESPONSE; + } + if (dc->properties & BT_GATT_CHRC_WRITE) { + props |= CHAR_PROP_WRITE; + } + if (dc->properties & BT_GATT_CHRC_NOTIFY) { + props |= CHAR_PROP_NOTIFY; + } + if (dc->properties & BT_GATT_CHRC_INDICATE) { + props |= CHAR_PROP_INDICATE; + } + + bleio_characteristic_obj_t *characteristic = + mp_obj_malloc(bleio_characteristic_obj_t, &bleio_characteristic_type); + + // Use max_length=20 for remote chars - reads go over the wire. + common_hal_bleio_characteristic_construct( + characteristic, service, + dc->value_handle, uuid, props, + SECURITY_MODE_OPEN, SECURITY_MODE_OPEN, + 20, false, NULL, NULL); + + if (count < max_chars) { + chars_out[count].characteristic = characteristic; + chars_out[count].decl_handle = dc->decl_handle; + } + count++; + + port_free(dc); + } + return count; +} + +// Helper to drain and free all nodes from the discovered_list. +static void free_discovered_list(void) { + sys_snode_t *node; + while ((node = sys_slist_get(&discovered_list)) != NULL) { + port_free(node); + } +} + +// The link dropped during discovery: the disconnect callback cleared +// connection->conn (so a subsequent bt_gatt_discover() would NULL-deref, since +// CONFIG_ASSERT is off) and/or aborted the active discovery (ctx->err set). +// Stop discovery cleanly with a "Not connected" exception instead of +// crashing or hanging on the spin loop. +static void bleio_discovery_check_connected(struct bt_conn *conn, + discovery_context_t *ctx) { + if (conn == NULL || ctx->err != 0) { + free_discovered_list(); + active_discovery_ctx = NULL; + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Not connected")); + } +} + +// Discover characteristics for a single remote service. +static void discover_characteristics_for_service(struct bt_conn *conn, + discovery_context_t *ctx, bleio_service_obj_t *service) { + // Bail cleanly if the link dropped before this phase began. + bleio_discovery_check_connected(conn, ctx); + // Need at least 2 handles: one for the service declaration, one for a characteristic + if (service->end_handle <= service->start_handle) { + return; + } + + sys_slist_init(&discovered_list); + ctx->done = false; + ctx->err = 0; + + memset(&discovery_params, 0, sizeof(discovery_params)); + discovery_params.uuid = NULL; + discovery_params.start_handle = service->start_handle + 1; + discovery_params.end_handle = service->end_handle; + discovery_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; + discovery_params.func = on_characteristic_discovered; + + int err = bt_gatt_discover(conn, &discovery_params); + if (err != 0) { + free_discovered_list(); + // -ENOENT means no characteristics found in the range, which is fine. + if (err == -ENOENT) { + return; + } + raise_zephyr_error(err); + } + + while (!ctx->done) { + RUN_BACKGROUND_TASKS; + } + + // The link may have dropped while we were waiting. + bleio_discovery_check_connected(conn, ctx); + + // Create CP objects outside of callback context where MP allocations are safe. + // This drains and frees the list nodes. + char_with_decl_t chars[16]; + size_t num_chars = create_characteristics_from_discovered(service, chars, 16); + + // Discover descriptors for each characteristic. + // The descriptor range for char[i] is from char[i].handle+1 to + // char[i+1].decl_handle-1 (or service.end_handle for the last). + for (size_t i = 0; i < num_chars && i < 16; i++) { + uint16_t desc_end; + if (i + 1 < num_chars) { + desc_end = chars[i + 1].decl_handle - 1; + } else { + desc_end = service->end_handle; + } + discover_descriptors_for_characteristic(conn, ctx, + chars[i].characteristic, desc_end); + } +} + +// ===== PAIRING / BONDING ===== + +// Auth info callbacks to track pairing completion/failure +static struct bt_conn_auth_info_cb auth_info_cb; +static bool auth_info_cb_registered = false; + +static void on_pairing_complete(struct bt_conn *conn, bool bonded) { + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + if (bleio_connections[i].conn == conn) { + bleio_connections[i].pair_status = PAIR_PAIRED; + bleio_connections[i].sec_err = 0; + break; + } + } +} + +static void on_pairing_failed(struct bt_conn *conn, enum bt_security_err reason) { + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + if (bleio_connections[i].conn == conn) { + bleio_connections[i].pair_status = PAIR_NOT_PAIRED; + bleio_connections[i].sec_err = (uint8_t)reason; + break; + } + } +} + +void bleio_connection_register_auth_callbacks(void) { + if (!auth_info_cb_registered) { + auth_info_cb.pairing_complete = on_pairing_complete; + auth_info_cb.pairing_failed = on_pairing_failed; + bt_conn_auth_info_cb_register(&auth_info_cb); + auth_info_cb_registered = true; + } +} void common_hal_bleio_connection_pair(bleio_connection_internal_t *self, bool bond) { - mp_raise_NotImplementedError(NULL); + (void)bond; // Bonding is enabled globally via CONFIG_BT_BONDABLE=y + + if (self == NULL || self->conn == NULL) { + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Not connected")); + } + + // Already paired? + if (self->pair_status == PAIR_PAIRED) { + return; + } + + // Ensure auth callbacks are registered + bleio_connection_register_auth_callbacks(); + + self->pair_status = PAIR_WAITING; + self->sec_err = 0; + + int err = bt_conn_set_security(self->conn, BT_SECURITY_L2); + if (err != 0) { + self->pair_status = PAIR_NOT_PAIRED; + raise_zephyr_error(err); + } + + // bt_conn_set_security may return 0 immediately if already encrypted + // (e.g., reconnection with stored bond keys). Check current security level. + if (bt_conn_get_security(self->conn) > BT_SECURITY_L1) { + self->pair_status = PAIR_PAIRED; + return; + } + + // Wait for pairing to complete. Zephyr's SMP reports each attempt via + // pairing_failed before auto-restarting security (smp.c), so a stale-bond + // retry shows up as an intermediate BT_SECURITY_ERR_PIN_OR_KEY_MISSING + // followed by a fresh attempt that can succeed. Ride through those and + // rely on Zephyr to signal the end: success (PAIR_PAIRED), SMP_TIMEOUT + // dropping the link (observed as conn == NULL), or a user interrupt. + while (self->pair_status != PAIR_PAIRED + && self->conn != NULL + && !mp_hal_is_interrupted()) { + RUN_BACKGROUND_TASKS; + } + + if (mp_hal_is_interrupted()) { + if (self->conn != NULL) { + bt_conn_auth_cancel(self->conn); + } + self->pair_status = PAIR_NOT_PAIRED; + return; + } + + if (self->pair_status == PAIR_PAIRED) { + return; + } + + // Reuse the messages shared with the nordic _bleio implementation + // (check_sec_status). A dropped link clears sec_err, so it falls through + // to the "unspecified issue" message alongside the explicit UNSPECIFIED + // error code rather than introducing a zephyr-specific "Pairing failed" + // string. + uint8_t sec_err_code = self->sec_err; + if (sec_err_code == BT_SECURITY_ERR_UNSPECIFIED || sec_err_code == 0) { + mp_raise_bleio_SecurityError(MP_ERROR_TEXT("Unspecified issue. The pairing request on the other device may have been declined or ignored.")); + } else { + mp_raise_bleio_SecurityError(MP_ERROR_TEXT("Unknown security error: 0x%04x"), sec_err_code); + } } void common_hal_bleio_connection_disconnect(bleio_connection_internal_t *self) { @@ -64,11 +571,130 @@ mp_int_t common_hal_bleio_connection_get_max_packet_length(bleio_connection_inte } bool common_hal_bleio_connection_get_paired(bleio_connection_obj_t *self) { - return false; + if (self == NULL || self->connection == NULL) { + return false; + } + return self->connection->pair_status == PAIR_PAIRED; } mp_obj_tuple_t *common_hal_bleio_connection_discover_remote_services(bleio_connection_obj_t *self, mp_obj_t service_uuids_whitelist) { - mp_raise_NotImplementedError(NULL); + bleio_connection_internal_t *connection = self->connection; + if (connection == NULL || connection->conn == NULL) { + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Not connected")); + } + + discovery_context_t ctx; + ctx.done = false; + ctx.err = 0; + + active_discovery_ctx = &ctx; + sys_slist_init(&discovered_list); + + // Create the result list for service objects. + mp_obj_list_t *service_list = mp_obj_new_list(0, NULL); + + // Discover primary services (callback appends nodes to discovered_list). + // When a whitelist is given, pass each UUID to Zephyr so filtering happens + // on the remote device — only matching services are returned. + if (service_uuids_whitelist == mp_const_none) { + memset(&discovery_params, 0, sizeof(discovery_params)); + discovery_params.uuid = NULL; + discovery_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE; + discovery_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE; + discovery_params.type = BT_GATT_DISCOVER_PRIMARY; + discovery_params.func = on_service_discovered; + + int err = bt_gatt_discover(connection->conn, &discovery_params); + if (err != 0) { + free_discovered_list(); + active_discovery_ctx = NULL; + raise_zephyr_error(err); + } + + while (!ctx.done) { + RUN_BACKGROUND_TASKS; + } + + // The link may have dropped during primary discovery. + bleio_discovery_check_connected(connection->conn, &ctx); + } else { + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(service_uuids_whitelist, &iter_buf); + mp_obj_t uuid_obj; + while ((uuid_obj = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + bleio_uuid_obj_t *cp_uuid = mp_arg_validate_type(uuid_obj, &bleio_uuid_type, MP_QSTR_uuid); + + memset(&discovery_params, 0, sizeof(discovery_params)); + discovery_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE; + discovery_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE; + discovery_params.type = BT_GATT_DISCOVER_PRIMARY; + discovery_params.func = on_service_discovered; + + // Set the Zephyr UUID filter. Stack variables are safe because + // bt_gatt_discover is synchronous (we spin until ctx.done). + union { + struct bt_uuid_16 u16; + struct bt_uuid_128 u128; + } z_uuid; + if (cp_uuid->size == BT_UUID_SIZE_16) { + z_uuid.u16.uuid.type = BT_UUID_TYPE_16; + z_uuid.u16.val = cp_uuid->uuid16; + discovery_params.uuid = &z_uuid.u16.uuid; + } else { + z_uuid.u128.uuid.type = BT_UUID_TYPE_128; + memcpy(z_uuid.u128.val, cp_uuid->uuid128, 16); + discovery_params.uuid = &z_uuid.u128.uuid; + } + + ctx.done = false; + ctx.err = 0; + + int err = bt_gatt_discover(connection->conn, &discovery_params); + if (err != 0) { + free_discovered_list(); + active_discovery_ctx = NULL; + raise_zephyr_error(err); + } + + while (!ctx.done) { + RUN_BACKGROUND_TASKS; + } + + // The link may have dropped during this UUID's discovery; stop + // before the next iteration clears ctx.err and crashes on a NULL conn. + bleio_discovery_check_connected(connection->conn, &ctx); + } + } + + // Create CP service objects outside callback context where MP allocations are safe. + // Drain and free the discovered service nodes. + mp_obj_list_t *result_list = service_list; + sys_snode_t *snode; + while ((snode = sys_slist_get(&discovered_list)) != NULL) { + discovered_service_t *ds = CONTAINER_OF(snode, discovered_service_t, node); + bleio_uuid_obj_t *uuid = bleio_uuid_from_zephyr(&ds->uuid.uuid); + bleio_service_obj_t *service = mp_obj_malloc(bleio_service_obj_t, &bleio_service_type); + common_hal_bleio_service_from_remote_service(service, self, uuid, false); + service->start_handle = ds->start_handle; + service->end_handle = ds->end_handle; + mp_obj_list_append(MP_OBJ_FROM_PTR(service_list), MP_OBJ_FROM_PTR(service)); + port_free(ds); + } + + // Discover characteristics for each service + for (size_t i = 0; i < result_list->len; i++) { + bleio_service_obj_t *svc = MP_OBJ_TO_PTR(result_list->items[i]); + if (svc->start_handle < svc->end_handle) { + // discover_characteristics_for_service re-checks, but guard here + // too so we don't enter the phase after a mid-loop disconnect. + bleio_discovery_check_connected(connection->conn, &ctx); + discover_characteristics_for_service(connection->conn, &ctx, svc); + } + } + + active_discovery_ctx = NULL; + + return mp_obj_new_tuple(result_list->len, result_list->items); } mp_float_t common_hal_bleio_connection_get_connection_interval(bleio_connection_internal_t *self) { diff --git a/ports/zephyr-cp/common-hal/_bleio/Connection.h b/ports/zephyr-cp/common-hal/_bleio/Connection.h index dc14125db5f..323e9393103 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Connection.h +++ b/ports/zephyr-cp/common-hal/_bleio/Connection.h @@ -13,9 +13,17 @@ struct bt_conn; +typedef enum { + PAIR_NOT_PAIRED, + PAIR_WAITING, + PAIR_PAIRED, +} pair_status_t; + typedef struct { struct bt_conn *conn; mp_obj_t connection_obj; + volatile pair_status_t pair_status; + uint8_t sec_err; // Security error code from pairing attempt } bleio_connection_internal_t; typedef struct { @@ -25,3 +33,4 @@ typedef struct { } bleio_connection_obj_t; mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t *connection); +void bleio_connection_register_auth_callbacks(void); diff --git a/ports/zephyr-cp/common-hal/_bleio/Descriptor.c b/ports/zephyr-cp/common-hal/_bleio/Descriptor.c index a3e65a5e006..a2219d8b5cd 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Descriptor.c +++ b/ports/zephyr-cp/common-hal/_bleio/Descriptor.c @@ -5,12 +5,48 @@ // // SPDX-License-Identifier: MIT +#include + #include "py/runtime.h" +#include "shared-bindings/_bleio/__init__.h" #include "shared-bindings/_bleio/Descriptor.h" #include "shared-bindings/_bleio/Characteristic.h" +#include "common-hal/_bleio/__init__.h" +#include "common-hal/_bleio/Characteristic.h" +#include "common-hal/_bleio/Connection.h" +#include "supervisor/port_heap.h" + +void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, + bleio_characteristic_obj_t *characteristic, bleio_uuid_obj_t *uuid, + bleio_attribute_security_mode_t read_perm, + bleio_attribute_security_mode_t write_perm, + mp_int_t max_length, bool fixed_length, + mp_buffer_info_t *initial_value_bufinfo) { + + self->characteristic = characteristic; + self->uuid = uuid; + self->read_perm = read_perm; + self->write_perm = write_perm; + self->max_length = max_length; + self->fixed_length = fixed_length; -void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_characteristic_obj_t *characteristic, bleio_uuid_obj_t *uuid, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo) { - mp_raise_NotImplementedError(NULL); + // Allocate value buffer + self->value = m_malloc(max_length); + memset(self->value, 0, max_length); + self->value_length = 0; + + // Copy initial value if provided + if (initial_value_bufinfo != NULL && initial_value_bufinfo->len > 0) { + size_t len = initial_value_bufinfo->len; + if (len > (size_t)max_length) { + len = max_length; + } + memcpy(self->value, initial_value_bufinfo->buf, len); + self->value_length = len; + } + + // Convert UUID to Zephyr format + bleio_uuid_to_zephyr(uuid, &self->zephyr_uuid); } bleio_uuid_obj_t *common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *self) { @@ -18,13 +54,56 @@ bleio_uuid_obj_t *common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *s } bleio_characteristic_obj_t *common_hal_bleio_descriptor_get_characteristic(bleio_descriptor_obj_t *self) { - mp_raise_NotImplementedError(NULL); + return (bleio_characteristic_obj_t *)self->characteristic; +} + +static bool descriptor_is_remote(bleio_descriptor_obj_t *self) { + return self->characteristic != NULL && + self->characteristic->service != NULL && + self->characteristic->service->is_remote; } size_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self, uint8_t *buf, size_t len) { - mp_raise_NotImplementedError(NULL); + if (descriptor_is_remote(self)) { + bleio_connection_obj_t *connection = + MP_OBJ_TO_PTR(self->characteristic->service->connection); + if (connection == NULL || connection->connection == NULL || + connection->connection->conn == NULL) { + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Not connected")); + } + + return bleio_gattc_read_sync(connection->connection->conn, + self->handle, buf, len); + } + + // Local descriptor + size_t copy_len = self->value_length; + if (copy_len > len) { + copy_len = len; + } + memcpy(buf, self->value, copy_len); + return copy_len; } void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buffer_info_t *bufinfo) { - mp_raise_NotImplementedError(NULL); + if (descriptor_is_remote(self)) { + bleio_connection_obj_t *connection = + MP_OBJ_TO_PTR(self->characteristic->service->connection); + if (connection == NULL || connection->connection == NULL || + connection->connection->conn == NULL) { + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Not connected")); + } + + bleio_gattc_write_sync(connection->connection->conn, + self->handle, bufinfo->buf, bufinfo->len); + return; + } + + // Local descriptor + size_t len = bufinfo->len; + if (len > self->max_length) { + len = self->max_length; + } + memcpy(self->value, bufinfo->buf, len); + self->value_length = len; } diff --git a/ports/zephyr-cp/common-hal/_bleio/Descriptor.h b/ports/zephyr-cp/common-hal/_bleio/Descriptor.h index 1d29cb27a50..b6d3ab4452e 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Descriptor.h +++ b/ports/zephyr-cp/common-hal/_bleio/Descriptor.h @@ -7,10 +7,15 @@ #pragma once +#include + #include "py/obj.h" #include "shared-bindings/_bleio/Attribute.h" #include "common-hal/_bleio/UUID.h" +// Forward declaration to break circular dependency +struct _bleio_characteristic_obj; + typedef struct _bleio_descriptor_obj { mp_obj_base_t base; bleio_uuid_obj_t *uuid; @@ -21,4 +26,6 @@ typedef struct _bleio_descriptor_obj { bool fixed_length; uint8_t *value; uint16_t value_length; + struct _bleio_characteristic_obj *characteristic; + struct bt_uuid_128 zephyr_uuid; } bleio_descriptor_obj_t; diff --git a/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c b/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c index 82fe8a3d176..8cdb5fcae76 100644 --- a/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c +++ b/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c @@ -5,62 +5,610 @@ // // SPDX-License-Identifier: MIT +#include + +#include +#include +#include + #include "py/runtime.h" +#include "py/stream.h" + +#include "shared/runtime/interrupt_char.h" + +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Connection.h" #include "shared-bindings/_bleio/PacketBuffer.h" +#include "shared-bindings/microcontroller/__init__.h" + +#include "supervisor/shared/tick.h" +#include "supervisor/port_heap.h" + +#include "common-hal/_bleio/Characteristic.h" +#include "common-hal/_bleio/PacketBuffer.h" +#include "common-hal/_bleio/Adapter.h" +#include "common-hal/_bleio/Connection.h" +#include "common-hal/_bleio/__init__.h" +#include "bindings/zephyr_kernel/__init__.h" + +// Zephyr's ring_buf is safe for single-producer/single-consumer without +// locks. The GATT callbacks (system workqueue) are the sole producer of +// incoming data; the CircuitPython VM (main thread) is the sole consumer. + +// Forward declarations. +static bool conn_is_valid(bleio_packet_buffer_obj_t *self); +static void packet_buffer_send_work_handler(struct k_work *work); +static void notify_complete_cb(struct bt_conn *conn, void *user_data); + +// Called from Zephyr GATT callbacks (system workqueue context). +// Wraps incoming data with a uint16_t length prefix and pushes into ringbuf. +// Tracks the first connection so notifications target the right peer. +void bleio_packet_buffer_extend(bleio_packet_buffer_obj_t *self, + struct bt_conn *conn, const uint8_t *data, size_t len) { + // Track the first/current connection for notifications. + if (conn != NULL) { + conn_is_valid(self); // clear stale conn if needed + if (self->conn == NULL) { + self->conn = conn; + } + } + if (len > UINT16_MAX) { + return; + } + + uint16_t packet_len = (uint16_t)len; + size_t total = sizeof(uint16_t) + len; + + // If the packet doesn't fit, drop oldest packets to make room. + while (ring_buf_space_get(&self->ringbuf) < total) { + uint16_t old_len; + if (ring_buf_size_get(&self->ringbuf) < sizeof(uint16_t)) { + // Not enough data for a length prefix, just reset. + ring_buf_reset(&self->ringbuf); + break; + } + uint32_t peeked = ring_buf_peek(&self->ringbuf, (uint8_t *)&old_len, sizeof(uint16_t)); + if (peeked < sizeof(uint16_t)) { + ring_buf_reset(&self->ringbuf); + break; + } + // Discard the length prefix + ring_buf_get(&self->ringbuf, (uint8_t *)&old_len, sizeof(uint16_t)); + // Discard the packet data + size_t to_discard = old_len; + if (to_discard > ring_buf_size_get(&self->ringbuf)) { + to_discard = ring_buf_size_get(&self->ringbuf); + } + uint8_t discard_buf[32]; + while (to_discard > 0) { + size_t chunk = to_discard > sizeof(discard_buf) ? sizeof(discard_buf) : to_discard; + ring_buf_get(&self->ringbuf, discard_buf, chunk); + to_discard -= chunk; + } + } + + ring_buf_put(&self->ringbuf, (uint8_t *)&packet_len, sizeof(uint16_t)); + ring_buf_put(&self->ringbuf, data, len); + + // Wake the VM background task so it drains the ring buffer promptly. + bleio_request_bluetooth_background(); +} + +void bleio_packet_buffer_set_conn(bleio_packet_buffer_obj_t *self, + struct bt_conn *conn) { + self->conn = conn; +} + +// Returns true if the tracked connection is still connected. +// Clears self->conn if the connection is stale. +static bool conn_is_valid(bleio_packet_buffer_obj_t *self) { + if (self->conn == NULL) { + return false; + } + struct bt_conn_info info; + if (bt_conn_get_info(self->conn, &info) != 0 || + info.state == BT_CONN_STATE_DISCONNECTED) { + self->conn = NULL; + return false; + } + return true; +} + +// Completion callback for bt_gatt_notify_cb. Runs on the system workqueue (per +// the Zephyr bt_gatt_notify_cb contract) — the same context send_work runs in +// — so packet_queued is single-threaded here and needs no lock. +static void notify_complete_cb(struct bt_conn *conn, void *user_data) { + (void)conn; + bleio_packet_buffer_obj_t *self = (bleio_packet_buffer_obj_t *)user_data; + self->packet_queued = false; + // Drain the other buffer now that this notify is done. K_NO_WAIT runs + // send_work immediately, cancelling any pending delayed retry. + k_work_reschedule(&self->send_work, K_NO_WAIT); +} + +// The deferred sender. Runs only on the system workqueue (submitted by the VM +// on write/flush and by notify_complete_cb on completion). It is the sole +// caller of bt_gatt_notify_cb, so packet_queued / pending_index / pending_size +// are touched here and in the VM's brief critical section — nowhere else. +static void packet_buffer_send_work_handler(struct k_work *work) { + bleio_packet_buffer_obj_t *self = CONTAINER_OF( + k_work_delayable_from_work(work), bleio_packet_buffer_obj_t, send_work); + + // A notification is awaiting its completion callback; it will resubmit us. + if (self->packet_queued) { + return; + } + if (self->pending_size == 0) { + return; // nothing staged + } + + // Server-side notify path only; clients write directly. characteristic is + // NULL after deinit, so bail before touching it (a completion callback can + // resubmit us after teardown). + bleio_characteristic_obj_t *c = self->characteristic; + if (c == NULL || self->client) { + return; + } + if (!conn_is_valid(self)) { + // Stale connection: drop everything staged. + self->pending_size = 0; + self->packet_queued = false; + return; + } + + // Staging keeps pending_size <= the negotiated ATT MTU payload, so the + // whole staged packet fits in one notification. + struct bt_gatt_notify_params params = { + .attr = &c->service->attrs[c->value_attr_index], + .data = self->outgoing[self->pending_index], + .len = self->pending_size, + .func = notify_complete_cb, + .user_data = self, + }; + + int err = bt_gatt_notify_cb(self->conn, ¶ms); + if (err == 0) { + // Accepted by the controller; the payload was copied into a PDU and + // will go out at the next connection event. Hand this buffer off and + // let the VM fill the other one while it's in flight. + self->packet_queued = true; + self->pending_size = 0; + self->pending_index ^= 1; // VM fills the other buffer next + return; + } + if (err == -ENOTCONN) { + // Peer disconnected — discard everything pending and cancel any + // pending delayed retry. (We're here only when packet_queued is clear, + // so no in-flight completion is owed.) + self->conn = NULL; + self->pending_size = 0; + self->packet_queued = false; + k_work_cancel_delayable(&self->send_work); + return; + } + // -ENOMEM / -EAGAIN: no ATT TX buffer right now. The ATT TX pool is shared + // across all ATT traffic on all connections, so it can be full from other + // notifies/indications/responses even when we have nothing in flight. + // Running on the workqueue makes the allocator use K_NO_WAIT: it returns + // NULL and the stack returns -ENOMEM *before* copying or queueing a PDU — + // nothing sent, nothing dropped; the bytes are still in + // outgoing[pending_index]. Leave the data staged and reschedule ourselves + // after a short delay so we retry even when the VM is idle (no write/flush + // to drive us). The delay — not an immediate resubmit — keeps the workqueue + // from busy-looping against a full pool. + k_work_reschedule(&self->send_work, K_MSEC(2)); +} + +// Shared core for both the Python-facing (allocating) and workflow +// (caller-supplied static buffer) constructors. Wires the ring buffer, the two +// outgoing buffers, the characteristic observer, and (for client-side) CCCD +// subscription. No GC heap allocation happens here. +static void packet_buffer_init_common(bleio_packet_buffer_obj_t *self, + bleio_characteristic_obj_t *characteristic, + uint8_t *ringbuf_data, size_t ringbuf_size, bool owns_ringbuf_data, + uint8_t *outgoing0, uint8_t *outgoing1, + bool owns_outgoing0, bool owns_outgoing1, + size_t max_packet_size) { + + self->characteristic = characteristic; + self->timeout_ms = 0; + self->max_packet_size = max_packet_size; + self->conn = NULL; + self->client = (characteristic->service != NULL && characteristic->service->is_remote); + self->outgoing[0] = outgoing0; + self->outgoing[1] = outgoing1; + self->owns_outgoing[0] = owns_outgoing0; + self->owns_outgoing[1] = owns_outgoing1; + self->pending_index = 0; + self->pending_size = 0; + self->packet_queued = false; + + self->ringbuf_data = ringbuf_data; + self->ringbuf_size = ringbuf_size; + self->owns_ringbuf_data = owns_ringbuf_data; + if (ringbuf_data != NULL && ringbuf_size > 0) { + ring_buf_init(&self->ringbuf, ringbuf_size, ringbuf_data); + } else { + // No incoming buffer (e.g. a server-side NOTIFY-only characteristic). + ring_buf_init(&self->ringbuf, 0, NULL); + } + + // The deferred sender drives server-side notifications. Client-side writes + // go out synchronously and never schedule it, but initializing it always is + // harmless and keeps flush()/deinit() simple. + k_work_init_delayable(&self->send_work, packet_buffer_send_work_handler); + + // Set ourselves as the characteristic's observer so GATT write/notify + // callbacks push incoming data into our ring buffer. + bleio_characteristic_set_observer(characteristic, MP_OBJ_FROM_PTR(self)); + + bleio_characteristic_properties_t props = + common_hal_bleio_characteristic_get_properties(characteristic); + + // For client-side characteristics with NOTIFY/INDICATE, subscribe to + // notifications from the remote peer. + if (self->client && (props & (CHAR_PROP_NOTIFY | CHAR_PROP_INDICATE))) { + bool do_notify = (props & CHAR_PROP_NOTIFY) != 0; + bool do_indicate = (props & CHAR_PROP_INDICATE) != 0; + common_hal_bleio_characteristic_set_cccd(characteristic, do_notify, do_indicate); + } +} void common_hal_bleio_packet_buffer_construct( bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, size_t buffer_size, size_t max_packet_size) { - (void)self; - (void)characteristic; - (void)buffer_size; - (void)max_packet_size; - mp_raise_NotImplementedError(NULL); + + bleio_characteristic_properties_t props = + common_hal_bleio_characteristic_get_properties(characteristic); + bool client = (characteristic->service != NULL && characteristic->service->is_remote); + + // Allocate ring buffer: buffer_size packets, each with 2-byte length prefix + size_t ringbuf_size = buffer_size * (sizeof(uint16_t) + max_packet_size); + uint8_t *ringbuf_data = port_malloc(ringbuf_size, false); + + // Allocate outgoing buffers. Client-side writes go out directly and only + // need one scratch buffer. Server-side notifications ping-pong between two + // buffers so the VM can fill one while the other is in flight. + uint8_t *outgoing0 = NULL; + uint8_t *outgoing1 = NULL; + bool owns0 = false; + bool owns1 = false; + if (client) { + outgoing0 = port_malloc(max_packet_size, false); + owns0 = true; + } else if (props & (CHAR_PROP_NOTIFY | CHAR_PROP_INDICATE)) { + outgoing0 = port_malloc(max_packet_size, false); + outgoing1 = port_malloc(max_packet_size, false); + owns0 = true; + owns1 = true; + } + + packet_buffer_init_common(self, characteristic, + ringbuf_data, ringbuf_size, true, + outgoing0, outgoing1, owns0, owns1, + max_packet_size); +} + +// Allocation-free version for BLE workflow use. The caller supplies static +// buffers so this can run before gc_init() without touching the GC heap. +// outgoing_buffer1 -> outgoing[0], outgoing_buffer2 -> outgoing[1]. +void _common_hal_bleio_packet_buffer_construct( + bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, + uint32_t *incoming_buffer, size_t incoming_buffer_size, + uint32_t *outgoing_buffer1, uint32_t *outgoing_buffer2, size_t max_packet_size, + ble_event_handler_t *static_handler_entry) { + (void)static_handler_entry; + + uint8_t *ringbuf_data = (uint8_t *)incoming_buffer; + size_t ringbuf_size = incoming_buffer_size; + + bleio_characteristic_properties_t props = + common_hal_bleio_characteristic_get_properties(characteristic); + bool client = (characteristic->service != NULL && characteristic->service->is_remote); + + uint8_t *outgoing0 = NULL; + uint8_t *outgoing1 = NULL; + bool owns0 = false; + bool owns1 = false; + + if (client) { + // Client-side: one scratch buffer for synchronous writes. + if (outgoing_buffer1 != NULL) { + outgoing0 = (uint8_t *)outgoing_buffer1; + } else { + outgoing0 = port_malloc(max_packet_size, false); + owns0 = true; + } + } else if (props & (CHAR_PROP_NOTIFY | CHAR_PROP_INDICATE)) { + // Server-side: two buffers to ping-pong between. + if (outgoing_buffer1 != NULL) { + outgoing0 = (uint8_t *)outgoing_buffer1; + } else { + outgoing0 = port_malloc(max_packet_size, false); + owns0 = true; + } + if (outgoing_buffer2 != NULL) { + outgoing1 = (uint8_t *)outgoing_buffer2; + } else { + outgoing1 = port_malloc(max_packet_size, false); + owns1 = true; + } + } + + packet_buffer_init_common(self, characteristic, + ringbuf_data, ringbuf_size, false, + outgoing0, outgoing1, owns0, owns1, + max_packet_size); } -mp_int_t common_hal_bleio_packet_buffer_write(bleio_packet_buffer_obj_t *self, const uint8_t *data, size_t len, uint8_t *header, size_t header_len) { - (void)self; - (void)data; - (void)len; - (void)header; - (void)header_len; - mp_raise_NotImplementedError(NULL); +mp_int_t common_hal_bleio_packet_buffer_readinto(bleio_packet_buffer_obj_t *self, + uint8_t *data, size_t len) { + // Need at least 2 bytes for the length prefix + if (ring_buf_size_get(&self->ringbuf) < sizeof(uint16_t)) { + return 0; + } + + // Peek at the packet length (don't consume yet) + uint16_t packet_length; + uint32_t peeked = ring_buf_peek(&self->ringbuf, (uint8_t *)&packet_length, sizeof(uint16_t)); + if (peeked < sizeof(uint16_t)) { + return 0; + } + + mp_int_t ret; + if (packet_length > len) { + // Packet is longer than requested. Return negative of overrun value. + ret = len - packet_length; + // Discard the packet + ring_buf_get(&self->ringbuf, (uint8_t *)&packet_length, sizeof(uint16_t)); + if (packet_length <= ring_buf_size_get(&self->ringbuf)) { + // Discard data in chunks + uint8_t discard[32]; + size_t remaining = packet_length; + while (remaining > 0) { + size_t chunk = remaining > sizeof(discard) ? sizeof(discard) : remaining; + ring_buf_get(&self->ringbuf, discard, chunk); + remaining -= chunk; + } + } + } else { + // Consume the length prefix + ring_buf_get(&self->ringbuf, (uint8_t *)&packet_length, sizeof(uint16_t)); + // Read packet data + ring_buf_get(&self->ringbuf, data, packet_length); + ret = packet_length; + } + + return ret; } -mp_int_t common_hal_bleio_packet_buffer_readinto(bleio_packet_buffer_obj_t *self, uint8_t *data, size_t len) { - (void)self; - (void)data; - (void)len; - mp_raise_NotImplementedError(NULL); +mp_int_t common_hal_bleio_packet_buffer_write(bleio_packet_buffer_obj_t *self, + const uint8_t *data, size_t len, uint8_t *header, size_t header_len) { + if (self->outgoing[0] == NULL) { + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Writes not supported on Characteristic")); + } + + mp_int_t outgoing_packet_length = + common_hal_bleio_packet_buffer_get_outgoing_packet_length(self); + if (outgoing_packet_length < 0) { + return -1; + } + + mp_int_t total_len = len + header_len; + if (total_len > outgoing_packet_length) { + mp_raise_ValueError_varg( + MP_ERROR_TEXT("Total data to write is larger than %q"), + MP_QSTR_outgoing_packet_length); + } + if (total_len > (mp_int_t)self->max_packet_size) { + mp_raise_ValueError_varg( + MP_ERROR_TEXT("Total data to write is larger than %q"), + MP_QSTR_max_packet_size); + } + + // Client-side (remote characteristic): write the request directly to the + // remote GATT server. The server-side path below stages into a double + // buffer and drains it from the system workqueue via send_work, so handle + // client writes separately (and synchronously). + if (self->characteristic != NULL && + self->characteristic->service != NULL && + self->characteristic->service->is_remote) { + bleio_characteristic_obj_t *c = self->characteristic; + bleio_connection_obj_t *connection = MP_OBJ_TO_PTR(c->service->connection); + if (connection == NULL || connection->connection == NULL || + connection->connection->conn == NULL) { + return -1; + } + struct bt_conn *conn = connection->connection->conn; + + // Combine header + data into the outgoing buffer (max_packet_size). + memcpy(self->outgoing[0], header, header_len); + memcpy(self->outgoing[0] + header_len, data, len); + size_t total = header_len + len; + + if (c->props & CHAR_PROP_WRITE_NO_RESPONSE) { + // Fire-and-forget write. Retry on transient "no TX buffer" + // (-EAGAIN) so paced protocols (e.g. BLE file transfer) don't + // silently drop data. + int err; + while ((err = bt_gatt_write_without_response(conn, c->handle, + self->outgoing[0], total, false)) == -EAGAIN) { + RUN_BACKGROUND_TASKS; + } + if (err != 0) { + raise_zephyr_error(err); + } + } else if (c->props & CHAR_PROP_WRITE) { + bleio_gattc_write_sync(conn, c->handle, self->outgoing[0], total); + } else { + // No write property; nothing to send. + return -1; + } + return (mp_int_t)total; + } + + // Server-side notify path: stage header + data into the pending buffer, + // then hand it to send_work on the system workqueue. bt_gatt_notify_cb + // blocks (K_FOREVER) off the workqueue, so we never call it here; send_work + // does, and the completion callback paces us to one notification in flight. + // The header prefixes each packet, so it's only written when the packet is + // empty (pending_size == 0). pending_size counts bytes staged but not yet + // accepted by the controller. + + if (mp_hal_is_interrupted()) { + return -1; + } + + // If this write would overflow the current packet, wait for send_work to + // drain it (which requires the in-flight notification to complete first). + if ((size_t)len + self->pending_size > (size_t)outgoing_packet_length) { + while (self->pending_size != 0 && + conn_is_valid(self) && + !mp_hal_is_interrupted()) { + k_work_reschedule(&self->send_work, K_NO_WAIT); + RUN_BACKGROUND_TASKS; + } + } + if (!conn_is_valid(self) || mp_hal_is_interrupted()) { + return -1; + } + + size_t num_bytes_written = 0; + + // send_work (system workqueue) may preempt us and modify pending_index / + // pending_size / packet_queued, so guard the append. + common_hal_mcu_disable_interrupts(); + { + uint8_t *pending = self->outgoing[self->pending_index]; + if (self->pending_size == 0 && header_len > 0) { + memcpy(pending, header, header_len); + self->pending_size += header_len; + num_bytes_written += header_len; + } + memcpy(pending + self->pending_size, data, len); + self->pending_size += len; + num_bytes_written += len; + } + common_hal_mcu_enable_interrupts(); + + // If no notification is in flight, kick the deferred sender to drain what + // we just staged (K_NO_WAIT runs it immediately, cancelling any pending + // delayed retry). If one is in flight, its completion will resubmit. + if (!self->packet_queued) { + k_work_reschedule(&self->send_work, K_NO_WAIT); + } + return (mp_int_t)num_bytes_written; } -mp_int_t common_hal_bleio_packet_buffer_get_incoming_packet_length(bleio_packet_buffer_obj_t *self) { - (void)self; - mp_raise_NotImplementedError(NULL); +mp_int_t common_hal_bleio_packet_buffer_get_incoming_packet_length( + bleio_packet_buffer_obj_t *self) { + if (self->characteristic == NULL) { + return -1; + } + + if (self->characteristic->service != NULL && + self->characteristic->service->is_remote && + self->characteristic->service->connection != mp_const_none && + (common_hal_bleio_characteristic_get_properties(self->characteristic) & + (CHAR_PROP_INDICATE | CHAR_PROP_NOTIFY))) { + // We are receiving from a remote service via NOTIFY/INDICATE. + bleio_connection_obj_t *connection = + MP_OBJ_TO_PTR(self->characteristic->service->connection); + if (connection != NULL && connection->connection != NULL && + common_hal_bleio_connection_get_connected(connection)) { + return common_hal_bleio_connection_get_max_packet_length(connection->connection); + } + return -1; + } + return self->characteristic->max_length; } -mp_int_t common_hal_bleio_packet_buffer_get_outgoing_packet_length(bleio_packet_buffer_obj_t *self) { - (void)self; - mp_raise_NotImplementedError(NULL); +mp_int_t common_hal_bleio_packet_buffer_get_outgoing_packet_length( + bleio_packet_buffer_obj_t *self) { + if (self->characteristic == NULL) { + return -1; + } + + if (self->characteristic->service != NULL && + !self->characteristic->service->is_remote && + (common_hal_bleio_characteristic_get_properties(self->characteristic) & + (CHAR_PROP_INDICATE | CHAR_PROP_NOTIFY))) { + // We are sending to a client via NOTIFY/INDICATE. The maximum payload + // per packet is bounded by the negotiated ATT MTU (ATT_MTU - 3 for the + // opcode and handle in a Handle Value Notification PDU). Without a + // current connection we can't know the MTU, so return -1 to signal + // that writes aren't possible yet. + if (!conn_is_valid(self)) { + return -1; + } + uint16_t mtu = bt_gatt_get_mtu(self->conn); + if (mtu < 3) { + return -1; + } + mp_int_t mtu_payload = (mp_int_t)mtu - 3; + return MIN(MIN(mtu_payload, (mp_int_t)self->max_packet_size), + (mp_int_t)self->characteristic->max_length); + } + // Writing to remote characteristic or local without NOTIFY + return MIN(self->characteristic->max_length, self->max_packet_size); } void common_hal_bleio_packet_buffer_flush(bleio_packet_buffer_obj_t *self) { - (void)self; - mp_raise_NotImplementedError(NULL); + // Wait until everything written has been handed to the controller AND its + // completion has fired (we pace to one notification in flight at a time). + // send_work runs on the system workqueue, which is higher priority than + // this thread, so k_work_reschedule(K_NO_WAIT) lets it preempt us; + // RUN_BACKGROUND_TASKS advances simulated time / lets completion callbacks + // fire so packet_queued clears. For client-side writes (synchronous) there + // is nothing pending, so this returns immediately. + while ((self->pending_size != 0 || self->packet_queued) && + conn_is_valid(self) && + !mp_hal_is_interrupted()) { + k_work_reschedule(&self->send_work, K_NO_WAIT); + RUN_BACKGROUND_TASKS; + } } bool common_hal_bleio_packet_buffer_deinited(bleio_packet_buffer_obj_t *self) { - return self->deinited; + return self->characteristic == NULL; } void common_hal_bleio_packet_buffer_deinit(bleio_packet_buffer_obj_t *self) { - if (self == NULL) { + if (common_hal_bleio_packet_buffer_deinited(self)) { return; } - self->deinited = true; + bleio_characteristic_clear_observer(self->characteristic); + self->characteristic = NULL; + // Cancel any pending delayed retry so it can't fire after we free the + // buffers (or after the object is gone). + k_work_cancel_delayable(&self->send_work); + // Free buffers if we own them (port_malloc'd). The BLE workflow path + // supplies static buffers that must not be freed. + if (self->owns_ringbuf_data && self->ringbuf_data != NULL) { + port_free(self->ringbuf_data); + } + self->ringbuf_data = NULL; + for (int i = 0; i < 2; i++) { + if (self->owns_outgoing[i] && self->outgoing[i] != NULL) { + port_free(self->outgoing[i]); + } + self->outgoing[i] = NULL; + } } bool common_hal_bleio_packet_buffer_connected(bleio_packet_buffer_obj_t *self) { - (void)self; - return false; + if (common_hal_bleio_packet_buffer_deinited(self)) { + return false; + } + // Check if the characteristic's connection is still active. + if (self->characteristic->service != NULL && + self->characteristic->service->is_remote) { + if (self->characteristic->service->connection == mp_const_none) { + return false; + } + bleio_connection_obj_t *connection = + MP_OBJ_TO_PTR(self->characteristic->service->connection); + return common_hal_bleio_connection_get_connected(connection); + } + return true; } diff --git a/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.h b/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.h index c8cd763fd61..67216775257 100644 --- a/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.h +++ b/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.h @@ -9,13 +9,71 @@ #include +#include +#include + #include "py/obj.h" +#include "shared-bindings/_bleio/Characteristic.h" -typedef struct _bleio_characteristic_obj bleio_characteristic_obj_t; +struct bt_conn; typedef void *ble_event_handler_t; typedef struct { mp_obj_base_t base; - bool deinited; + bleio_characteristic_obj_t *characteristic; + uint32_t timeout_ms; + // Store the connection we are talking to. We expect to talk over a single + // connection at a time. + struct bt_conn *conn; + bool client; + // The current MTU negotiated with the active connection. + size_t max_packet_size; + + // ringbuf to store incoming packets. + struct ring_buf ringbuf; + uint8_t *ringbuf_data; + size_t ringbuf_size; + + // Outgoing path. Two fixed buffers alternate so the VM can keep filling one + // while the other is in flight with the controller. + // + // outgoing[pending_index] is the buffer the VM is filling (pending_size + // bytes staged, not yet sent). packet_queued means a notification has been + // accepted by the controller and its completion callback hasn't fired yet + // — at most one is in flight at a time. + // + uint8_t *outgoing[2]; + uint8_t pending_index; // which buffer the VM is filling + volatile uint16_t pending_size; // bytes staged in outgoing[pending_index] + volatile bool packet_queued; // a notification is in flight + + // send_work is a delayable work item, the only caller of bt_gatt_notify_cb + // (that API blocks (K_FOREVER) off the workqueue but returns -ENOMEM from + // it). It's rescheduled with K_NO_WAIT to drain after a write/completion, + // and with a short delay on -ENOMEM so it self-retries even when the VM is + // idle and the shared ATT TX pool is full from other traffic. It and the + // completion callback both run on the workqueue (serialized with each + // other); the VM is the only other toucher of these fields and guards its + // read-modify-write with common_hal_mcu_disable_interrupts() (the workqueue + // preempts the VM, so the critical section keeps it out while we append). + // bt_gatt_notify_cb copies the payload into a PDU on success, so the source + // buffer may be reused once the call returns 0; the completion callback + // paces us to one in flight and makes flush() accurate. + struct k_work_delayable send_work; + + // Ownership: true if the buffer was port_malloc'd and should be freed in + // deinit; false if it is a caller-supplied static buffer (the BLE workflow + // path, which runs before gc_init()). + bool owns_ringbuf_data; + bool owns_outgoing[2]; } bleio_packet_buffer_obj_t; + +// Called from GATT callbacks (system workqueue context) to push +// data into the PacketBuffer ring buffer with length-prefix framing. +void bleio_packet_buffer_extend(bleio_packet_buffer_obj_t *self, + struct bt_conn *conn, const uint8_t *data, size_t len); + +// Called from CCCD write callback to record the subscribing connection. +void bleio_packet_buffer_set_conn(bleio_packet_buffer_obj_t *self, + struct bt_conn *conn); diff --git a/ports/zephyr-cp/common-hal/_bleio/Service.c b/ports/zephyr-cp/common-hal/_bleio/Service.c index cefc85b6df6..5f0c7ee268b 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Service.c +++ b/ports/zephyr-cp/common-hal/_bleio/Service.c @@ -5,24 +5,129 @@ // // SPDX-License-Identifier: MIT +// GATT server service implementation for Zephyr. +// +// Each bleio Service owns a dynamically-allocated array of bt_gatt_attr +// (the Zephyr GATT attribute table). When a Characteristic is added, we +// append 2-4 attrs (declaration, value, optional CCC, optional CUD) and +// (re-)register the service with bt_gatt_service_register(). +// +// IMPORTANT: Zephyr's BT_UUID_GATT_PRIMARY / BT_UUID_GATT_CHRC / … macros +// expand to compound-literal pointers. Inside a function those have automatic +// storage duration and become dangling once the function returns. We therefore +// declare file-scope static const UUIDs (_uuid_primary, _uuid_chrc, …) and +// reference those from every bt_gatt_attr we build. + +#include +#include + +#include + +#include "py/gc.h" #include "py/runtime.h" -#include "shared-bindings/_bleio/Service.h" +#include "supervisor/port_heap.h" +#include "bindings/zephyr_kernel/__init__.h" #include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Service.h" +#include "common-hal/_bleio/__init__.h" +#include "common-hal/_bleio/Characteristic.h" + +#define INITIAL_ATTR_CAPACITY 16 + +// Standard GATT UUIDs with static storage duration. +// BT_UUID_GATT_PRIMARY etc. expand to compound literals which have automatic +// storage when used inside functions - the resulting pointers dangle after the +// function returns. These static constants persist for the lifetime of the +// program. +static const struct bt_uuid_16 _uuid_primary = BT_UUID_INIT_16(BT_UUID_GATT_PRIMARY_VAL); +static const struct bt_uuid_16 _uuid_secondary = BT_UUID_INIT_16(BT_UUID_GATT_SECONDARY_VAL); +static const struct bt_uuid_16 _uuid_chrc = BT_UUID_INIT_16(BT_UUID_GATT_CHRC_VAL); +static const struct bt_uuid_16 _uuid_ccc = BT_UUID_INIT_16(BT_UUID_GATT_CCC_VAL); +static const struct bt_uuid_16 _uuid_cud = BT_UUID_INIT_16(BT_UUID_GATT_CUD_VAL); + +static void service_ensure_capacity(bleio_service_obj_t *self, size_t needed) { + if (self->attr_count + needed <= self->attr_capacity) { + return; + } + size_t new_capacity = self->attr_capacity; + while (new_capacity < self->attr_count + needed) { + new_capacity *= 2; + } + struct bt_gatt_attr *new_attrs = port_realloc(self->attrs, + new_capacity * sizeof(struct bt_gatt_attr), false); + self->attrs = new_attrs; + self->attr_capacity = new_capacity; +} + +uint32_t _common_hal_bleio_service_construct(bleio_service_obj_t *self, + bleio_uuid_obj_t *uuid, bool is_secondary, + mp_obj_list_t *characteristic_list) { + self->uuid = uuid; + self->is_secondary = is_secondary; + self->is_remote = false; + self->connection = mp_const_none; + self->characteristic_list = characteristic_list; + self->start_handle = 0; + self->end_handle = 0; + self->registered = false; + + // Convert UUID to Zephyr format + bleio_uuid_to_zephyr(uuid, &self->zephyr_uuid); + + // Allocate attrs array + self->attr_capacity = INITIAL_ATTR_CAPACITY; + self->attrs = port_malloc(self->attr_capacity * sizeof(struct bt_gatt_attr), false); + memset(self->attrs, 0, self->attr_capacity * sizeof(struct bt_gatt_attr)); + self->attr_count = 0; -uint32_t _common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary, mp_obj_list_t *characteristic_list) { - mp_raise_NotImplementedError(NULL); + // Add primary/secondary service declaration at index 0 + const struct bt_uuid *svc_type_uuid = is_secondary + ? (const struct bt_uuid *)&_uuid_secondary + : (const struct bt_uuid *)&_uuid_primary; + self->attrs[0] = (struct bt_gatt_attr) { + .uuid = svc_type_uuid, + .perm = BT_GATT_PERM_READ, + .read = bt_gatt_attr_read_service, + .user_data = (void *)&self->zephyr_uuid, + }; + self->attr_count = 1; + + return 0; } -void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary) { - mp_raise_NotImplementedError(NULL); +void common_hal_bleio_service_construct(bleio_service_obj_t *self, + bleio_uuid_obj_t *uuid, bool is_secondary) { + mp_obj_list_t *char_list = mp_obj_new_list(0, NULL); + _common_hal_bleio_service_construct(self, uuid, is_secondary, char_list); } void common_hal_bleio_service_deinit(bleio_service_obj_t *self) { - // Nothing to do + if (self->registered) { + bt_gatt_service_unregister(&self->zephyr_service); + self->registered = false; + } + if (self->attrs != NULL) { + port_free(self->attrs); + self->attrs = NULL; + self->attr_capacity = 0; + self->attr_count = 0; + } } -void common_hal_bleio_service_from_remote_service(bleio_service_obj_t *self, bleio_connection_obj_t *connection, bleio_uuid_obj_t *uuid, bool is_secondary) { - mp_raise_NotImplementedError(NULL); +void common_hal_bleio_service_from_remote_service(bleio_service_obj_t *self, + bleio_connection_obj_t *connection, bleio_uuid_obj_t *uuid, bool is_secondary) { + self->uuid = uuid; + self->is_secondary = is_secondary; + self->is_remote = true; + self->connection = MP_OBJ_FROM_PTR(connection); + self->characteristic_list = mp_obj_new_list(0, NULL); + self->start_handle = 0; + self->end_handle = 0; + self->registered = false; + self->attrs = NULL; + self->attr_count = 0; + self->attr_capacity = 0; + bleio_uuid_to_zephyr(uuid, &self->zephyr_uuid); } bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self) { @@ -41,6 +146,120 @@ bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self) { return self->is_secondary; } -void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *initial_value_bufinfo, const char *user_description) { - mp_raise_NotImplementedError(NULL); +void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, + bleio_characteristic_obj_t *characteristic, + mp_buffer_info_t *initial_value_bufinfo, + const char *user_description) { + + if (self->registered) { + bt_gatt_service_unregister(&self->zephyr_service); + self->registered = false; + } + + // Calculate how many attrs we need: + // 1 = characteristic declaration, 1 = value attr + // +1 if NOTIFY|INDICATE (CCC descriptor) + // +1 if user_description + size_t attrs_needed = 2; + bool needs_ccc = (characteristic->props & (CHAR_PROP_NOTIFY | CHAR_PROP_INDICATE)) != 0; + bool needs_user_desc = (user_description != NULL && user_description[0] != '\0'); + if (needs_ccc) { + attrs_needed++; + } + if (needs_user_desc) { + attrs_needed++; + } + + service_ensure_capacity(self, attrs_needed); + + // Map CP properties to Zephyr BT spec properties + uint8_t zephyr_props = 0; + if (characteristic->props & CHAR_PROP_BROADCAST) { + zephyr_props |= BT_GATT_CHRC_BROADCAST; + } + if (characteristic->props & CHAR_PROP_READ) { + zephyr_props |= BT_GATT_CHRC_READ; + } + if (characteristic->props & CHAR_PROP_WRITE_NO_RESPONSE) { + zephyr_props |= BT_GATT_CHRC_WRITE_WITHOUT_RESP; + } + if (characteristic->props & CHAR_PROP_WRITE) { + zephyr_props |= BT_GATT_CHRC_WRITE; + } + if (characteristic->props & CHAR_PROP_NOTIFY) { + zephyr_props |= BT_GATT_CHRC_NOTIFY; + } + if (characteristic->props & CHAR_PROP_INDICATE) { + zephyr_props |= BT_GATT_CHRC_INDICATE; + } + + // Set up the Zephyr chrc struct in the characteristic + characteristic->zephyr_chrc.uuid = &characteristic->zephyr_uuid.uuid; + characteristic->zephyr_chrc.value_handle = 0; + characteristic->zephyr_chrc.properties = zephyr_props; + + // Map permissions + uint16_t perm = bleio_security_to_zephyr_perm( + characteristic->read_perm, characteristic->write_perm, characteristic->props); + + // Attr: characteristic declaration + size_t idx = self->attr_count; + self->attrs[idx] = (struct bt_gatt_attr) { + .uuid = (const struct bt_uuid *)&_uuid_chrc, + .perm = BT_GATT_PERM_READ, + .read = bt_gatt_attr_read_chrc, + .user_data = &characteristic->zephyr_chrc, + }; + idx++; + + // Attr: characteristic value (UUID points into characteristic, which persists) + characteristic->value_attr_index = idx; + self->attrs[idx] = (struct bt_gatt_attr) { + .uuid = &characteristic->zephyr_uuid.uuid, + .perm = perm, + .read = (characteristic->props & CHAR_PROP_READ) ? bleio_char_read_cb : NULL, + .write = (characteristic->props & (CHAR_PROP_WRITE | CHAR_PROP_WRITE_NO_RESPONSE)) ? bleio_char_write_cb : NULL, + .user_data = characteristic, + }; + idx++; + + // Attr: CCC descriptor (for NOTIFY/INDICATE) + if (needs_ccc) { + characteristic->zephyr_ccc = (struct bt_gatt_ccc_managed_user_data) + BT_GATT_CCC_MANAGED_USER_DATA_INIT(bleio_ccc_changed_cb, bleio_ccc_write_cb, NULL); + self->attrs[idx] = (struct bt_gatt_attr) { + .uuid = (const struct bt_uuid *)&_uuid_ccc, + .perm = BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, + .read = bt_gatt_attr_read_ccc, + .write = bt_gatt_attr_write_ccc, + .user_data = &characteristic->zephyr_ccc, + }; + idx++; + } + + // Attr: user description descriptor + if (needs_user_desc) { + self->attrs[idx] = (struct bt_gatt_attr) { + .uuid = (const struct bt_uuid *)&_uuid_cud, + .perm = BT_GATT_PERM_READ, + .read = bt_gatt_attr_read_cud, + .user_data = (void *)user_description, + }; + idx++; + } + + self->attr_count = idx; + + // Add characteristic to list + mp_obj_list_append(MP_OBJ_FROM_PTR(self->characteristic_list), + MP_OBJ_FROM_PTR(characteristic)); + + // Register the service + self->zephyr_service.attrs = self->attrs; + self->zephyr_service.attr_count = self->attr_count; + int err = bt_gatt_service_register(&self->zephyr_service); + if (err != 0) { + raise_zephyr_error(err); + } + self->registered = true; } diff --git a/ports/zephyr-cp/common-hal/_bleio/Service.h b/ports/zephyr-cp/common-hal/_bleio/Service.h index 86727d3b0f7..8c820fd204e 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Service.h +++ b/ports/zephyr-cp/common-hal/_bleio/Service.h @@ -7,6 +7,8 @@ #pragma once +#include + #include "py/obj.h" #include "py/objlist.h" #include "common-hal/_bleio/UUID.h" @@ -20,4 +22,11 @@ typedef struct bleio_service_obj { uint16_t end_handle; bool is_remote; bool is_secondary; + // Zephyr GATT server fields: + struct bt_gatt_service zephyr_service; + struct bt_gatt_attr *attrs; + size_t attr_count; + size_t attr_capacity; + struct bt_uuid_128 zephyr_uuid; + bool registered; } bleio_service_obj_t; diff --git a/ports/zephyr-cp/common-hal/_bleio/UUID.c b/ports/zephyr-cp/common-hal/_bleio/UUID.c index 916eedb2c47..9e963b1b0bc 100644 --- a/ports/zephyr-cp/common-hal/_bleio/UUID.c +++ b/ports/zephyr-cp/common-hal/_bleio/UUID.c @@ -11,27 +11,25 @@ #include "shared-bindings/_bleio/UUID.h" void common_hal_bleio_uuid_construct(bleio_uuid_obj_t *self, mp_int_t uuid16, const uint8_t uuid128[16]) { - if (uuid16 != 0) { - // 16-bit UUID - self->size = 16; - // Convert 16-bit UUID to 128-bit - // Bluetooth Base UUID: 00000000-0000-1000-8000-00805F9B34FB - const uint8_t base_uuid[16] = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - memcpy(self->uuid128, base_uuid, 16); - self->uuid128[12] = (uuid16 & 0xff); + self->uuid16 = (uint16_t)uuid16; + if (uuid128 == NULL) { + // 16-bit UUID — only bytes 12-13 matter; rest is zero (matching ble_hci). + self->size = BT_UUID_SIZE_16; + memset(self->uuid128, 0, 16); + self->uuid128[12] = uuid16 & 0xff; self->uuid128[13] = (uuid16 >> 8) & 0xff; } else { - // 128-bit UUID - self->size = 128; + // 128-bit UUID — uuid128 has bytes 12-13 zeroed, uuid16 is extracted from them + self->size = BT_UUID_SIZE_128; memcpy(self->uuid128, uuid128, 16); + // Restore the 16-bit portion from uuid16 into bytes 12-13 (little-endian). + self->uuid128[12] = uuid16 & 0xff; + self->uuid128[13] = uuid16 >> 8; } } uint32_t common_hal_bleio_uuid_get_uuid16(bleio_uuid_obj_t *self) { - if (self->size == 16) { - return (self->uuid128[13] << 8) | self->uuid128[12]; - } - return 0; + return self->uuid16; } void common_hal_bleio_uuid_get_uuid128(bleio_uuid_obj_t *self, uint8_t uuid128[16]) { @@ -39,11 +37,11 @@ void common_hal_bleio_uuid_get_uuid128(bleio_uuid_obj_t *self, uint8_t uuid128[1 } uint32_t common_hal_bleio_uuid_get_size(bleio_uuid_obj_t *self) { - return self->size; + return self->size == BT_UUID_SIZE_16 ? 16 : 128; } void common_hal_bleio_uuid_pack_into(bleio_uuid_obj_t *self, uint8_t *buf) { - if (self->size == 16) { + if (self->size == BT_UUID_SIZE_16) { buf[0] = self->uuid128[12]; buf[1] = self->uuid128[13]; } else { diff --git a/ports/zephyr-cp/common-hal/_bleio/UUID.h b/ports/zephyr-cp/common-hal/_bleio/UUID.h index 386f5a7b8b9..9a0743a1b60 100644 --- a/ports/zephyr-cp/common-hal/_bleio/UUID.h +++ b/ports/zephyr-cp/common-hal/_bleio/UUID.h @@ -7,10 +7,13 @@ #pragma once +#include + #include "py/obj.h" typedef struct { mp_obj_base_t base; uint8_t uuid128[16]; - uint8_t size; + uint8_t size; // BT_UUID_SIZE_16 (2) or BT_UUID_SIZE_128 (16) + uint16_t uuid16; } bleio_uuid_obj_t; diff --git a/ports/zephyr-cp/common-hal/_bleio/__init__.c b/ports/zephyr-cp/common-hal/_bleio/__init__.c index 719564c1cd4..e3c208a7d1d 100644 --- a/ports/zephyr-cp/common-hal/_bleio/__init__.c +++ b/ports/zephyr-cp/common-hal/_bleio/__init__.c @@ -9,7 +9,11 @@ #include "shared-bindings/_bleio/__init__.h" #include "shared-bindings/_bleio/Adapter.h" #include "common-hal/_bleio/Adapter.h" +#include "common-hal/_bleio/__init__.h" +#include "bindings/zephyr_kernel/__init__.h" +#include "common-hal/_bleio/Connection.h" #include "supervisor/shared/bluetooth/bluetooth.h" +#include "supervisor/shared/tick.h" // The singleton _bleio.Adapter object bleio_adapter_obj_t common_hal_bleio_adapter_obj; @@ -17,6 +21,7 @@ bleio_adapter_obj_t common_hal_bleio_adapter_obj; void common_hal_bleio_init(void) { common_hal_bleio_adapter_obj.base.type = &bleio_adapter_type; bleio_adapter_reset(&common_hal_bleio_adapter_obj); + bleio_connection_register_auth_callbacks(); } void bleio_user_reset(void) { @@ -46,6 +51,132 @@ void common_hal_bleio_gc_collect(void) { bleio_adapter_gc_collect(&common_hal_bleio_adapter_obj); } -void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist) { - mp_raise_NotImplementedError(NULL); +// ======================================================================= +// Shared synchronous GATT helpers +// ======================================================================= + +typedef struct { + uint8_t *buf; + size_t buf_len; + size_t read_len; + volatile bool done; + volatile int err; +} gattc_read_ctx_t; + +typedef struct { + volatile bool done; + volatile int err; +} gattc_write_ctx_t; + +static gattc_read_ctx_t *active_read_ctx; +static gattc_write_ctx_t *active_write_ctx; +static struct bt_gatt_read_params read_params; +static struct bt_gatt_write_params write_params; + +static uint8_t on_gattc_read(struct bt_conn *conn, uint8_t err, + struct bt_gatt_read_params *params, + const void *data, uint16_t length) { + gattc_read_ctx_t *ctx = active_read_ctx; + if (ctx == NULL) { + return BT_GATT_ITER_STOP; + } + + if (err) { + ctx->err = err; + ctx->done = true; + return BT_GATT_ITER_STOP; + } + + if (data == NULL || length == 0) { + ctx->done = true; + return BT_GATT_ITER_STOP; + } + + size_t copy_len = length; + if (ctx->read_len + copy_len > ctx->buf_len) { + copy_len = ctx->buf_len - ctx->read_len; + } + if (copy_len > 0) { + memcpy(ctx->buf + ctx->read_len, data, copy_len); + ctx->read_len += copy_len; + } + + ctx->done = true; + return BT_GATT_ITER_STOP; +} + +static void on_gattc_write(struct bt_conn *conn, uint8_t err, + struct bt_gatt_write_params *params) { + gattc_write_ctx_t *ctx = active_write_ctx; + if (ctx == NULL) { + return; + } + ctx->err = err; + ctx->done = true; +} + +size_t bleio_gattc_read_sync(struct bt_conn *conn, uint16_t handle, + uint8_t *buf, size_t len) { + gattc_read_ctx_t ctx = { + .buf = buf, + .buf_len = len, + .read_len = 0, + .done = false, + .err = 0, + }; + active_read_ctx = &ctx; + + memset(&read_params, 0, sizeof(read_params)); + read_params.func = on_gattc_read; + read_params.handle_count = 1; + read_params.single.handle = handle; + read_params.single.offset = 0; + + int err = bt_gatt_read(conn, &read_params); + if (err != 0) { + active_read_ctx = NULL; + raise_zephyr_error(err); + } + + while (!ctx.done) { + RUN_BACKGROUND_TASKS; + } + active_read_ctx = NULL; + + if (ctx.err != 0) { + raise_zephyr_error(ctx.err); + } + + return ctx.read_len; +} + +void bleio_gattc_write_sync(struct bt_conn *conn, uint16_t handle, + const uint8_t *data, size_t len) { + gattc_write_ctx_t ctx = { + .done = false, + .err = 0, + }; + active_write_ctx = &ctx; + + memset(&write_params, 0, sizeof(write_params)); + write_params.func = on_gattc_write; + write_params.handle = handle; + write_params.offset = 0; + write_params.data = data; + write_params.length = len; + + int err = bt_gatt_write(conn, &write_params); + if (err != 0) { + active_write_ctx = NULL; + raise_zephyr_error(err); + } + + while (!ctx.done) { + RUN_BACKGROUND_TASKS; + } + active_write_ctx = NULL; + + if (ctx.err != 0) { + raise_zephyr_error(ctx.err); + } } diff --git a/ports/zephyr-cp/common-hal/_bleio/__init__.h b/ports/zephyr-cp/common-hal/_bleio/__init__.h index 1502767c615..72dc249d142 100644 --- a/ports/zephyr-cp/common-hal/_bleio/__init__.h +++ b/ports/zephyr-cp/common-hal/_bleio/__init__.h @@ -7,4 +7,41 @@ #pragma once -// Placeholder for Zephyr-specific BLE defines +#include + +#include +#include +#include + +#include "common-hal/_bleio/UUID.h" + +// Convert a CircuitPython bleio UUID to a Zephyr bt_uuid stored in caller- +// provided bt_uuid_128 storage. For 16-bit UUIDs we reinterpret the storage +// as bt_uuid_16 and write .val (at offset 2, matching Zephyr's struct layout). +// For 128-bit UUIDs the 16-byte value is copied directly into bt_uuid_128.val. +// The result can be used as `&out->uuid` wherever a `const struct bt_uuid *` +// is needed; Zephyr dispatches on the .type field at runtime. +static inline void bleio_uuid_to_zephyr(const bleio_uuid_obj_t *cp_uuid, + struct bt_uuid_128 *out) { + if (cp_uuid->size == BT_UUID_SIZE_16) { + out->uuid.type = BT_UUID_TYPE_16; + ((struct bt_uuid_16 *)out)->val = + (cp_uuid->uuid128[13] << 8) | cp_uuid->uuid128[12]; + } else { + out->uuid.type = BT_UUID_TYPE_128; + // Both CP and Zephyr store UUID bytes in little-endian order. + memcpy(out->val, cp_uuid->uuid128, 16); + } +} + +// Shared synchronous GATT helpers — wrap Zephyr's async bt_gatt_read/write +// into a blocking call with RUN_BACKGROUND_TASKS spinning. +size_t bleio_gattc_read_sync(struct bt_conn *conn, uint16_t handle, + uint8_t *buf, size_t len); +void bleio_gattc_write_sync(struct bt_conn *conn, uint16_t handle, + const uint8_t *data, size_t len); + +// Abort any in-flight remote GATT discovery; called from the disconnect +// callback so discover_remote_services() fails cleanly instead of hanging +// or NULL-dereferencing the cleared connection. +void bleio_connection_discovery_abort(void); diff --git a/ports/zephyr-cp/common-hal/busio/UART.c b/ports/zephyr-cp/common-hal/busio/UART.c index 9940853da50..af1de0e9023 100644 --- a/ports/zephyr-cp/common-hal/busio/UART.c +++ b/ports/zephyr-cp/common-hal/busio/UART.c @@ -30,9 +30,7 @@ static void serial_cb(const struct device *dev, void *user_data) { uint8_t c; - if (!uart_irq_update(dev)) { - return; - } + uart_irq_update(dev); if (!uart_irq_rx_ready(dev)) { return; diff --git a/ports/zephyr-cp/common-hal/microcontroller/Processor.c b/ports/zephyr-cp/common-hal/microcontroller/Processor.c index 593d73bcc61..61beb470595 100644 --- a/ports/zephyr-cp/common-hal/microcontroller/Processor.c +++ b/ports/zephyr-cp/common-hal/microcontroller/Processor.c @@ -14,6 +14,34 @@ #include #include +#if defined(CONFIG_WIFI) +#include + +// Fallback for SoCs with no hwinfo driver, where hwinfo_get_device_id() returns +// -ENOSYS and cpu.uid would otherwise be all zeros. A WiFi link address is +// per-device, so it is a better answer than nothing, and at least one vendor +// (Silicon Labs, for the siwx91x) defines that part's unique ID as exactly this: +// the WiFi MAC zero-extended to eight bytes. +// +// This is not a burned-in serial. A MAC can generally be reprogrammed by vendor +// tooling, so cpu.uid must not be relied on as tamper proof where it comes from +// here. Boards whose SoC has a real hwinfo driver never reach this path. +static ssize_t uid_from_wifi_mac(uint8_t raw_id[]) { + if (COMMON_HAL_MCU_PROCESSOR_UID_LENGTH < 8) { + return -1; + } + struct net_if *iface = net_if_get_first_wifi(); + struct net_linkaddr *addr = (iface != NULL) ? net_if_get_link_addr(iface) : NULL; + if (addr == NULL || addr->len != 6) { + return -1; + } + raw_id[0] = 0; + raw_id[1] = 0; + memcpy(&raw_id[2], addr->addr, 6); + return 8; +} +#endif + float common_hal_mcu_processor_get_temperature(void) { return 0.0; @@ -34,6 +62,14 @@ float common_hal_mcu_processor_get_voltage(void) { void common_hal_mcu_processor_get_uid(uint8_t raw_id[]) { ssize_t len = hwinfo_get_device_id(raw_id, COMMON_HAL_MCU_PROCESSOR_UID_LENGTH); + #if defined(CONFIG_WIFI) + if (len < 0) { + ssize_t mac_len = uid_from_wifi_mac(raw_id); + if (mac_len > 0) { + len = mac_len; + } + } + #endif if (len < 0) { printk("UID retrieval failed: %d\n", len); len = 0; diff --git a/ports/zephyr-cp/common-hal/socketpool/Socket.c b/ports/zephyr-cp/common-hal/socketpool/Socket.c index ca8ba419839..bb626857a98 100644 --- a/ports/zephyr-cp/common-hal/socketpool/Socket.c +++ b/ports/zephyr-cp/common-hal/socketpool/Socket.c @@ -27,6 +27,7 @@ #include #include +#include #include #define SOCKETPOOL_IP_STR_LEN 48 @@ -82,7 +83,12 @@ static void socketpool_resolve_host_or_throw(int family, int type, const char *h int error = zsock_getaddrinfo(hostname, service_buf, &hints, &result_i); if (error != 0 || result_i == NULL) { - common_hal_socketpool_socketpool_raise_gaierror_noname(); + // Report what the resolver said. A null result with no error is the + // one case that really is "not found". + if (error == 0) { + error = DNS_EAI_NONAME; + } + common_hal_socketpool_socketpool_raise_gaierror(error); } memcpy(addr, result_i->ai_addr, sizeof(struct sockaddr_storage)); @@ -165,7 +171,7 @@ static bool _socketpool_socket(socketpool_socketpool_obj_t *self, } // Sockets should be nonblocking in most cases. - if (zsock_fcntl(socknum, F_SETFL, O_NONBLOCK) < 0) { + if (zsock_fcntl(socknum, F_SETFL, ZVFS_O_NONBLOCK) < 0) { // Ignore if non-blocking is unsupported. } @@ -217,15 +223,6 @@ int socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out, timed_out = supervisor_ticks_ms64() - start_ticks >= self->timeout_ms; } RUN_BACKGROUND_TASKS; - #if CIRCUITPY_HOSTNETWORK - if (self->timeout_ms == 0) { - struct zsock_timeval tv = { - .tv_sec = 0, - .tv_usec = 1000, - }; - zsock_setsockopt(self->num, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); - } - #endif newsoc = zsock_accept(self->num, (struct sockaddr *)&peer_addr, &socklen); // In non-blocking mode, fail instead of timing out if (newsoc == -1 && (self->timeout_ms == 0 || mp_hal_is_interrupted())) { @@ -245,7 +242,7 @@ int socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out, } // We got a socket. New client socket will not be non-blocking by default, so make it non-blocking. - if (zsock_fcntl(newsoc, F_SETFL, O_NONBLOCK) < 0) { + if (zsock_fcntl(newsoc, F_SETFL, ZVFS_O_NONBLOCK) < 0) { // Ignore if non-blocking is unsupported. } diff --git a/ports/zephyr-cp/common-hal/socketpool/SocketPool.c b/ports/zephyr-cp/common-hal/socketpool/SocketPool.c index c9a3f008321..2df1af69187 100644 --- a/ports/zephyr-cp/common-hal/socketpool/SocketPool.c +++ b/ports/zephyr-cp/common-hal/socketpool/SocketPool.c @@ -20,6 +20,7 @@ #include #include +#include void common_hal_socketpool_socketpool_construct(socketpool_socketpool_obj_t *self, mp_obj_t radio) { bool is_wifi = false; @@ -104,7 +105,10 @@ mp_obj_t common_hal_socketpool_getaddrinfo_raise(socketpool_socketpool_obj_t *se struct zsock_addrinfo *res = NULL; int err = socketpool_getaddrinfo_common(host, port, &hints, &res); if (err != 0 || res == NULL) { - common_hal_socketpool_socketpool_raise_gaierror_noname(); + if (err == 0) { + err = DNS_EAI_NONAME; + } + common_hal_socketpool_socketpool_raise_gaierror(err); } nlr_buf_t nlr; diff --git a/ports/zephyr-cp/common-hal/usb_hid/Device.c b/ports/zephyr-cp/common-hal/usb_hid/Device.c new file mode 100644 index 00000000000..ba263d44d74 --- /dev/null +++ b/ports/zephyr-cp/common-hal/usb_hid/Device.c @@ -0,0 +1,150 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2018 hathach for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include +#include + +#include "py/gc.h" +#include "py/runtime.h" +#include "shared-bindings/usb_hid/Device.h" +#include "shared-module/usb_hid/__init__.h" +#include "shared-module/usb_hid/Device.h" +#include "shared-module/usb_hid/report_descriptors.h" +#include "common-hal/usb_hid/__init__.h" +#include "supervisor/shared/tick.h" + +const usb_hid_device_obj_t usb_hid_device_keyboard_obj = { + .base = { + .type = &usb_hid_device_type, + }, + .report_descriptor = keyboard_report_descriptor, + .report_descriptor_length = sizeof(keyboard_report_descriptor), + .usage_page = 0x01, + .usage = 0x06, + .num_report_ids = 1, + .report_ids = { 0x01, }, + .in_report_lengths = { 8, }, + .out_report_lengths = { 1, }, +}; + +const usb_hid_device_obj_t usb_hid_device_mouse_obj = { + .base = { + .type = &usb_hid_device_type, + }, + .report_descriptor = mouse_report_descriptor, + .report_descriptor_length = sizeof(mouse_report_descriptor), + .usage_page = 0x01, + .usage = 0x02, + .num_report_ids = 1, + .report_ids = { 0x02, }, + .in_report_lengths = { 4, }, + .out_report_lengths = { 0, }, +}; + +const usb_hid_device_obj_t usb_hid_device_consumer_control_obj = { + .base = { + .type = &usb_hid_device_type, + }, + .report_descriptor = consumer_control_report_descriptor, + .report_descriptor_length = sizeof(consumer_control_report_descriptor), + .usage_page = 0x0C, + .usage = 0x01, + .num_report_ids = 1, + .report_ids = { 0x03 }, + .in_report_lengths = { 2, }, + .out_report_lengths = { 0, }, +}; + +char *custom_usb_hid_interface_name; + +static size_t get_report_id_idx(usb_hid_device_obj_t *self, size_t report_id) { + for (size_t i = 0; i < self->num_report_ids; i++) { + if (report_id == self->report_ids[i]) { + return i; + } + } + return CIRCUITPY_USB_HID_MAX_REPORT_IDS_PER_DESCRIPTOR; +} + +uint8_t common_hal_usb_hid_device_validate_report_id(usb_hid_device_obj_t *self, mp_int_t report_id_arg) { + if (report_id_arg == -1 && self->num_report_ids == 1) { + return self->report_ids[0]; + } + if (!(report_id_arg >= 0 && + get_report_id_idx(self, (size_t)report_id_arg) < CIRCUITPY_USB_HID_MAX_REPORT_IDS_PER_DESCRIPTOR)) { + mp_arg_error_invalid(MP_QSTR_report_id); + } + return (uint8_t)report_id_arg; +} + +void common_hal_usb_hid_device_construct(usb_hid_device_obj_t *self, mp_obj_t report_descriptor, uint16_t usage_page, uint16_t usage, size_t num_report_ids, uint8_t *report_ids, uint8_t *in_report_lengths, uint8_t *out_report_lengths) { + mp_arg_validate_length_max( + num_report_ids, CIRCUITPY_USB_HID_MAX_REPORT_IDS_PER_DESCRIPTOR, MP_QSTR_report_ids); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(report_descriptor, &bufinfo, MP_BUFFER_READ); + self->report_descriptor_length = bufinfo.len; + + uint8_t *descriptor_bytes = gc_alloc(bufinfo.len, false); + memcpy(descriptor_bytes, bufinfo.buf, bufinfo.len); + self->report_descriptor = descriptor_bytes; + + self->usage_page = usage_page; + self->usage = usage; + self->num_report_ids = num_report_ids; + memcpy(self->report_ids, report_ids, num_report_ids); + memcpy(self->in_report_lengths, in_report_lengths, num_report_ids); + memcpy(self->out_report_lengths, out_report_lengths, num_report_ids); +} + +uint16_t common_hal_usb_hid_device_get_usage_page(usb_hid_device_obj_t *self) { + return self->usage_page; +} + +uint16_t common_hal_usb_hid_device_get_usage(usb_hid_device_obj_t *self) { + return self->usage; +} + +void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t *report, uint8_t len, uint8_t report_id) { + size_t id_idx = get_report_id_idx(self, report_id); + + mp_arg_validate_length(len, self->in_report_lengths[id_idx], MP_QSTR_report); + + // Wait until interface is ready, timeout = 2 seconds + uint64_t end_ticks = supervisor_ticks_ms64() + 2000; + while ((supervisor_ticks_ms64() < end_ticks) && !usb_hid_ready()) { + RUN_BACKGROUND_TASKS; + } + + if (!usb_hid_submit_report(report_id, report, len)) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("USB error")); + } +} + +mp_obj_t common_hal_usb_hid_device_get_last_received_report(usb_hid_device_obj_t *self, uint8_t report_id) { + size_t id_idx = get_report_id_idx(self, report_id); + if (!self->out_report_buffers_updated[id_idx]) { + return mp_const_none; + } + self->out_report_buffers_updated[id_idx] = false; + return mp_obj_new_bytes(self->out_report_buffers[id_idx], self->out_report_lengths[id_idx]); +} + +void usb_hid_device_create_report_buffers(usb_hid_device_obj_t *self) { + for (size_t i = 0; i < self->num_report_ids; i++) { + self->in_report_buffers[i] = + self->in_report_lengths[i] > 0 + ? gc_alloc(self->in_report_lengths[i], false) + : NULL; + + self->out_report_buffers[i] = + self->out_report_lengths[i] > 0 + ? gc_alloc(self->out_report_lengths[i], false) + : NULL; + } + memset(self->out_report_buffers_updated, 0, sizeof(self->out_report_buffers_updated)); +} diff --git a/ports/zephyr-cp/common-hal/usb_hid/__init__.c b/ports/zephyr-cp/common-hal/usb_hid/__init__.c new file mode 100644 index 00000000000..552a91e102b --- /dev/null +++ b/ports/zephyr-cp/common-hal/usb_hid/__init__.c @@ -0,0 +1,377 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include + +#include "py/gc.h" +#include "py/runtime.h" +#include "shared-bindings/usb_hid/__init__.h" +#include "shared-bindings/usb_hid/Device.h" +#include "shared-module/usb_hid/Device.h" +#include "supervisor/port.h" + +#include +#include +#include +#include +#include + +#define MAX_HID_DEVICES 8 + +static usb_hid_device_obj_t hid_devices[MAX_HID_DEVICES]; +// If 0, USB HID is disabled. +static mp_int_t num_hid_devices; +// Which boot device is available? 0: no boot devices, 1: boot keyboard, 2: boot mouse. +static uint8_t hid_boot_device; +// Whether a boot device was requested by a SET_PROTOCOL request from the host. +static bool hid_boot_device_requested; +// This tuple is stored in usb_hid.devices. +static mp_obj_tuple_t *hid_devices_tuple; + +// Whether the HID interface is ready (USB configured). +static bool hid_interface_ready; + +static mp_obj_tuple_t default_hid_devices_tuple = { + .base = { + .type = &mp_type_tuple, + }, + .len = 3, + .items = { + MP_OBJ_FROM_PTR(&usb_hid_device_keyboard_obj), + MP_OBJ_FROM_PTR(&usb_hid_device_mouse_obj), + MP_OBJ_FROM_PTR(&usb_hid_device_consumer_control_obj), + }, +}; + +// These describe the standard descriptors used for boot keyboard and mouse, +// which don't use report IDs. +static const usb_hid_device_obj_t boot_keyboard_obj = { + .base = { + .type = &usb_hid_device_type, + }, + .report_descriptor = NULL, + .report_descriptor_length = 0, + .usage_page = 0x01, + .usage = 0x06, + .num_report_ids = 1, + .report_ids = { 0, }, + .in_report_lengths = { 8, }, + .out_report_lengths = { 1, }, +}; + +static const usb_hid_device_obj_t boot_mouse_obj = { + .base = { + .type = &usb_hid_device_type, + }, + .report_descriptor = NULL, + .report_descriptor_length = 0, + .usage_page = 0x01, + .usage = 0x02, + .num_report_ids = 1, + .report_ids = { 0, }, + .in_report_lengths = { 4, }, + .out_report_lengths = { 0, }, +}; + +bool usb_hid_enabled(void) { + return num_hid_devices > 0; +} + +uint8_t usb_hid_boot_device(void) { + return hid_boot_device; +} + +// Returns 1 or 2 if host requested a boot device and boot protocol was enabled. +uint8_t common_hal_usb_hid_get_boot_device(void) { + return hid_boot_device_requested ? hid_boot_device : 0; +} + +void usb_hid_set_defaults(void) { + hid_boot_device = 0; + hid_boot_device_requested = false; + hid_interface_ready = false; + common_hal_usb_hid_enable( + CIRCUITPY_USB_HID_ENABLED_DEFAULT ? &default_hid_devices_tuple : mp_const_empty_tuple, 0); +} + +// Make up a fresh tuple containing the device objects saved in the static +// devices table. Save the tuple in usb_hid.devices. +static void usb_hid_set_devices_from_hid_devices(void) { + mp_obj_t tuple_items[num_hid_devices]; + for (mp_int_t i = 0; i < num_hid_devices; i++) { + tuple_items[i] = &hid_devices[i]; + } + hid_devices_tuple = mp_obj_new_tuple(num_hid_devices, tuple_items); + usb_hid_set_devices(hid_devices_tuple); +} + +bool common_hal_usb_hid_disable(void) { + return common_hal_usb_hid_enable(mp_const_empty_tuple, 0); +} + +bool common_hal_usb_hid_enable(const mp_obj_t devices, uint8_t boot_device) { + // We can't change the devices once USB is connected. + if (hid_interface_ready) { + return false; + } + + const mp_int_t num_devices = MP_OBJ_SMALL_INT_VALUE(mp_obj_len(devices)); + mp_arg_validate_length_max(num_devices, MAX_HID_DEVICES, MP_QSTR_devices); + + num_hid_devices = num_devices; + hid_boot_device = boot_device; + + // Remember the devices in static storage so they live across VMs. + for (mp_int_t i = 0; i < num_hid_devices; i++) { + usb_hid_device_obj_t *device = + MP_OBJ_TO_PTR(mp_obj_subscr(devices, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL)); + memcpy(&hid_devices[i], device, sizeof(usb_hid_device_obj_t)); + } + + usb_hid_set_devices_from_hid_devices(); + + return true; +} + +// Called when HID devices are ready to be used, when code.py or the REPL starts running. +void usb_hid_setup_devices(void) { + // If the host requested a boot device, replace the current list of devices + // with a single-element tuple containing the proper boot device. + if (hid_boot_device_requested) { + memcpy(&hid_devices[0], + hid_boot_device == 1 ? &boot_keyboard_obj : &boot_mouse_obj, + sizeof(usb_hid_device_obj_t)); + num_hid_devices = 1; + } + + usb_hid_set_devices_from_hid_devices(); + + // Create report buffers on the heap. + for (mp_int_t i = 0; i < num_hid_devices; i++) { + usb_hid_device_create_report_buffers(&hid_devices[i]); + } +} + +// Build the combined HID report descriptor from all devices. +static uint8_t *hid_report_descriptor = NULL; + +size_t usb_hid_report_descriptor_length(void) { + size_t total_hid_report_descriptor_length = 0; + for (mp_int_t i = 0; i < num_hid_devices; i++) { + total_hid_report_descriptor_length += hid_devices[i].report_descriptor_length; + } + return total_hid_report_descriptor_length; +} + +void usb_hid_build_report_descriptor(void) { + if (!usb_hid_enabled()) { + return; + } + size_t report_length = usb_hid_report_descriptor_length(); + hid_report_descriptor = port_malloc(report_length, false); + if (hid_report_descriptor == NULL) { + return; + } + + uint8_t *report_descriptor_start = hid_report_descriptor; + for (mp_int_t i = 0; i < num_hid_devices; i++) { + usb_hid_device_obj_t *device = &hid_devices[i]; + memcpy(report_descriptor_start, device->report_descriptor, device->report_descriptor_length); + report_descriptor_start += device->report_descriptor_length; + // Clear the heap pointer to the bytes of the descriptor. + device->report_descriptor = NULL; + } +} + +void usb_hid_gc_collect(void) { + gc_collect_ptr(hid_devices_tuple); + + for (mp_int_t device_idx = 0; device_idx < num_hid_devices; device_idx++) { + gc_collect_ptr((void *)hid_devices[device_idx].report_descriptor); + + for (size_t id_idx = 0; id_idx < hid_devices[device_idx].num_report_ids; id_idx++) { + gc_collect_ptr(hid_devices[device_idx].in_report_buffers[id_idx]); + gc_collect_ptr(hid_devices[device_idx].out_report_buffers[id_idx]); + } + } +} + +bool usb_hid_get_device_with_report_id(uint8_t report_id, usb_hid_device_obj_t **device_out, size_t *id_idx_out) { + for (uint8_t device_idx = 0; device_idx < num_hid_devices; device_idx++) { + usb_hid_device_obj_t *device = &hid_devices[device_idx]; + for (size_t id_idx = 0; id_idx < device->num_report_ids; id_idx++) { + if (device->report_ids[id_idx] == report_id) { + *device_out = device; + *id_idx_out = id_idx; + return true; + } + } + } + return false; +} + +// ===== Zephyr USBD HID Device Callbacks ===== + +static void hid_iface_ready(const struct device *dev, const bool ready) { + hid_interface_ready = ready; +} + +static int hid_get_report(const struct device *dev, + const uint8_t type, const uint8_t id, + const uint16_t len, uint8_t *const buf) { + // Support Input Report and Feature Report + if (type != HID_REPORT_TYPE_INPUT && type != HID_REPORT_TYPE_FEATURE) { + return -ENOTSUP; + } + + usb_hid_device_obj_t *hid_device; + size_t id_idx; + if (!usb_hid_get_device_with_report_id(id, &hid_device, &id_idx)) { + return 0; + } + if (!hid_device->in_report_buffers[id_idx]) { + return 0; + } + + uint8_t in_len = hid_device->in_report_lengths[id_idx]; + const uint8_t *src = hid_device->in_report_buffers[id_idx]; + + // When the descriptor uses Report IDs (id != 0), the on-wire report is + // prefixed with the Report ID byte. Zephyr sends the buffer verbatim, so + // prepend the ID here. id == 0 means no Report ID item -> no prefix. + if (id != 0) { + if (len == 0) { + return 0; + } + buf[0] = id; + uint16_t copy_len = len - 1; + if (copy_len > in_len) { + copy_len = in_len; + } + memcpy(&buf[1], src, copy_len); + return 1 + copy_len; + } + + uint16_t copy_len = len; + if (copy_len > in_len) { + copy_len = in_len; + } + memcpy(buf, src, copy_len); + return copy_len; +} + +static int hid_set_report(const struct device *dev, + const uint8_t type, const uint8_t id, + const uint16_t len, const uint8_t *const buf) { + uint8_t report_id = id; + const uint8_t *data = buf; + uint16_t data_len = len; + + if (report_id != 0 && len > 0) { + // Descriptor uses Report IDs: the on-wire SET_REPORT data stage is + // prefixed with the Report ID byte. Zephyr passes the raw payload, so + // strip the leading ID byte (it should match the id from wValue). + if (buf[0] == report_id) { + data = &buf[1]; + data_len = len - 1; + } + } else if (report_id == 0 && len > 0) { + // id==0 normally means no Report ID item (no prefix). But if no device + // has report ID 0 with a matching out length, treat the first byte as a + // report ID (host sent a prefixed report despite id==0). + usb_hid_device_obj_t *hid0 = NULL; + size_t idx0; + if (!(usb_hid_get_device_with_report_id(0, &hid0, &idx0) && + hid0 && hid0->out_report_buffers[idx0] && + hid0->out_report_lengths[idx0] == len)) { + report_id = buf[0]; + data = &buf[1]; + data_len = len - 1; + } + } + + // Look up the device for the resolved report_id. + usb_hid_device_obj_t *hid_device = NULL; + size_t id_idx; + if (!usb_hid_get_device_with_report_id(report_id, &hid_device, &id_idx)) { + return -EINVAL; + } + + if (hid_device->out_report_buffers[id_idx] && + hid_device->out_report_lengths[id_idx] == data_len) { + memcpy(hid_device->out_report_buffers[id_idx], data, data_len); + hid_device->out_report_buffers_updated[id_idx] = true; + return 0; + } + + return -EINVAL; +} + +static void hid_set_protocol(const struct device *dev, const uint8_t proto) { + hid_boot_device_requested = (proto == HID_PROTOCOL_BOOT); +} + +static struct hid_device_ops hid_ops = { + .iface_ready = hid_iface_ready, + .get_report = hid_get_report, + .set_report = hid_set_report, + .set_protocol = hid_set_protocol, +}; + +// ===== HID Device Registration ===== + +// Get the Zephyr HID device for submitting reports +static const struct device *hid_zephyr_dev; + +const struct device *usb_hid_get_zephyr_device(void) { + return hid_zephyr_dev; +} + +void usb_hid_init_devices(void) { + if (!usb_hid_enabled()) { + return; + } + + hid_zephyr_dev = DEVICE_DT_GET_ONE(zephyr_hid_device); + if (!device_is_ready(hid_zephyr_dev)) { + return; + } + + usb_hid_build_report_descriptor(); + if (hid_report_descriptor == NULL) { + return; + } + + int ret = hid_device_register(hid_zephyr_dev, + hid_report_descriptor, + usb_hid_report_descriptor_length(), + &hid_ops); + if (ret != 0) { + return; + } +} + +bool usb_hid_ready(void) { + return hid_zephyr_dev != NULL && hid_interface_ready; +} + +// Submit an input report through Zephyr's HID device. +// Called from common_hal_usb_hid_device_send_report(). +bool usb_hid_submit_report(uint8_t report_id, const uint8_t *report, uint8_t len) { + if (hid_zephyr_dev == NULL || !hid_interface_ready) { + return false; + } + + // Send report_id zero without a prefix + if (report_id == 0) { + return hid_device_submit_report(hid_zephyr_dev, len, report) == 0; + } + + uint8_t __attribute__((aligned(sizeof(void *)))) report_buffer[1 + len]; + report_buffer[0] = report_id; + memcpy(&report_buffer[1], report, len); + return hid_device_submit_report(hid_zephyr_dev, len + 1, report_buffer) == 0; +} diff --git a/ports/zephyr-cp/common-hal/usb_hid/__init__.h b/ports/zephyr-cp/common-hal/usb_hid/__init__.h new file mode 100644 index 00000000000..1985ea29549 --- /dev/null +++ b/ports/zephyr-cp/common-hal/usb_hid/__init__.h @@ -0,0 +1,28 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-module/usb_hid/Device.h" +#include "supervisor/usb.h" + +extern usb_hid_device_obj_t usb_hid_devices[]; + +bool usb_hid_enabled(void); +uint8_t usb_hid_boot_device(void); +void usb_hid_set_defaults(void); +void usb_hid_setup_devices(void); +size_t usb_hid_report_descriptor_length(void); +void usb_hid_build_report_descriptor(void); +bool usb_hid_get_device_with_report_id(uint8_t report_id, usb_hid_device_obj_t **device_out, size_t *id_idx_out); +void usb_hid_gc_collect(void); + +// For supervisor/usb.c integration +void usb_hid_init_devices(void); + +// For Device.c send_report +bool usb_hid_ready(void); +bool usb_hid_submit_report(uint8_t report_id, const uint8_t *report, uint8_t len); diff --git a/ports/zephyr-cp/common-hal/wifi/Network.c b/ports/zephyr-cp/common-hal/wifi/Network.c index 44c049f88c1..510f8b9f5c2 100644 --- a/ports/zephyr-cp/common-hal/wifi/Network.c +++ b/ports/zephyr-cp/common-hal/wifi/Network.c @@ -34,35 +34,44 @@ mp_obj_t common_hal_wifi_network_get_country(wifi_network_obj_t *self) { } mp_obj_t common_hal_wifi_network_get_authmode(wifi_network_obj_t *self) { + // Translate Zephyr's wifi_security_type. An empty list would read as an + // open network to any caller checking for AUTHMODE_OPEN. uint32_t authmode_mask = 0; - // switch (self->record.authmode) { - // case WIFI_AUTH_OPEN: - // authmode_mask = AUTHMODE_OPEN; - // break; - // case WIFI_AUTH_WEP: - // authmode_mask = AUTHMODE_WEP; - // break; - // case WIFI_AUTH_WPA_PSK: - // authmode_mask = AUTHMODE_WPA | AUTHMODE_PSK; - // break; - // case WIFI_AUTH_WPA2_PSK: - // authmode_mask = AUTHMODE_WPA2 | AUTHMODE_PSK; - // break; - // case WIFI_AUTH_WPA_WPA2_PSK: - // authmode_mask = AUTHMODE_WPA | AUTHMODE_WPA2 | AUTHMODE_PSK; - // break; - // case WIFI_AUTH_WPA2_ENTERPRISE: - // authmode_mask = AUTHMODE_WPA2 | AUTHMODE_ENTERPRISE; - // break; - // case WIFI_AUTH_WPA3_PSK: - // authmode_mask = AUTHMODE_WPA3 | AUTHMODE_PSK; - // break; - // case WIFI_AUTH_WPA2_WPA3_PSK: - // authmode_mask = AUTHMODE_WPA2 | AUTHMODE_WPA3 | AUTHMODE_PSK; - // break; - // default: - // break; - // } + switch (self->scan_result.security) { + case WIFI_SECURITY_TYPE_NONE: + authmode_mask = AUTHMODE_OPEN; + break; + case WIFI_SECURITY_TYPE_WEP: + authmode_mask = AUTHMODE_WEP; + break; + case WIFI_SECURITY_TYPE_WPA_PSK: + authmode_mask = AUTHMODE_WPA | AUTHMODE_PSK; + break; + case WIFI_SECURITY_TYPE_PSK: + case WIFI_SECURITY_TYPE_PSK_SHA256: + authmode_mask = AUTHMODE_WPA2 | AUTHMODE_PSK; + break; + case WIFI_SECURITY_TYPE_SAE: // == WIFI_SECURITY_TYPE_SAE_HNP (alias) + case WIFI_SECURITY_TYPE_SAE_H2E: + case WIFI_SECURITY_TYPE_SAE_AUTO: + case WIFI_SECURITY_TYPE_SAE_EXT_KEY: + case WIFI_SECURITY_TYPE_FT_SAE: + authmode_mask = AUTHMODE_WPA3 | AUTHMODE_PSK; + break; + case WIFI_SECURITY_TYPE_WPA_AUTO_PERSONAL: + authmode_mask = AUTHMODE_WPA | AUTHMODE_WPA2 | AUTHMODE_WPA3 | AUTHMODE_PSK; + break; + case WIFI_SECURITY_TYPE_EAP: // == WIFI_SECURITY_TYPE_EAP_TLS (alias) + case WIFI_SECURITY_TYPE_EAP_PEAP_MSCHAPV2: + case WIFI_SECURITY_TYPE_EAP_PEAP_GTC: + case WIFI_SECURITY_TYPE_EAP_TTLS_MSCHAPV2: + case WIFI_SECURITY_TYPE_EAP_PEAP_TLS: + case WIFI_SECURITY_TYPE_FT_EAP: + authmode_mask = AUTHMODE_WPA2 | AUTHMODE_ENTERPRISE; + break; + default: + break; + } mp_obj_t authmode_list = mp_obj_new_list(0, NULL); if (authmode_mask != 0) { for (uint8_t i = 0; i < 32; i++) { diff --git a/ports/zephyr-cp/common-hal/wifi/Radio.c b/ports/zephyr-cp/common-hal/wifi/Radio.c index 35a0b76a362..a85f6bbb736 100644 --- a/ports/zephyr-cp/common-hal/wifi/Radio.c +++ b/ports/zephyr-cp/common-hal/wifi/Radio.c @@ -25,14 +25,25 @@ #include "bindings/zephyr_kernel/__init__.h" #include +#include +#include +// dns_resolve_get_default() for radio.ipv4_dns. +#include #include +#if CIRCUITPY_WIFI_PING +// net_icmp_* for radio.ping(). +#include +#endif #include #include +#include #if CIRCUITPY_MDNS #include "common-hal/mdns/Server.h" #endif +LOG_MODULE_DECLARE(cp_wifi); + #define MAC_ADDRESS_LENGTH 6 // static void set_mode_station(wifi_radio_obj_t *self, bool state) { @@ -85,7 +96,7 @@ void common_hal_wifi_radio_set_enabled(wifi_radio_obj_t *self, bool enabled) { // #if CIRCUITPY_MDNS // mdns_server_deinit_singleton(); // #endif - printk("net_if_down\n"); + LOG_DBG("net_if_down"); int res = net_if_down(self->sta_netif); if (res < 0 && res != -EALREADY) { raise_zephyr_error(res); @@ -94,7 +105,7 @@ void common_hal_wifi_radio_set_enabled(wifi_radio_obj_t *self, bool enabled) { return; } if (!self->started && enabled) { - printk("net_if_up\n"); + LOG_DBG("net_if_up"); int res = net_if_up(self->sta_netif); if (res < 0 && res != -EALREADY) { raise_zephyr_error(res); @@ -118,8 +129,13 @@ void common_hal_wifi_radio_set_hostname(wifi_radio_obj_t *self, const char *host } mp_obj_t common_hal_wifi_radio_get_mac_address(wifi_radio_obj_t *self) { - uint8_t mac[MAC_ADDRESS_LENGTH]; - // esp_wifi_get_mac(ESP_IF_WIFI_STA, mac); + uint8_t mac[MAC_ADDRESS_LENGTH] = { 0 }; + if (self->sta_netif != NULL) { + struct net_linkaddr *addr = net_if_get_link_addr(self->sta_netif); + if (addr != NULL && addr->len >= MAC_ADDRESS_LENGTH) { + memcpy(mac, addr->addr, MAC_ADDRESS_LENGTH); + } + } return mp_obj_new_bytes(mac, MAC_ADDRESS_LENGTH); } @@ -214,13 +230,11 @@ void common_hal_wifi_radio_set_mac_address_ap(wifi_radio_obj_t *self, const uint } mp_obj_t common_hal_wifi_radio_start_scanning_networks(wifi_radio_obj_t *self, uint8_t start_channel, uint8_t stop_channel) { - printk("common_hal_wifi_radio_start_scanning_networks\n"); + LOG_DBG("common_hal_wifi_radio_start_scanning_networks"); if (self->current_scan != NULL) { - printk("Already scanning for wifi networks\n"); mp_raise_RuntimeError(MP_ERROR_TEXT("Already scanning for wifi networks")); } if (!common_hal_wifi_radio_get_enabled(self)) { - printk("WiFi is not enabled\n"); mp_raise_RuntimeError(MP_ERROR_TEXT("WiFi is not enabled")); } @@ -246,12 +260,12 @@ mp_obj_t common_hal_wifi_radio_start_scanning_networks(wifi_radio_obj_t *self, u K_POLL_MODE_NOTIFY_ONLY, &scan->msgq); wifi_scannednetworks_scan_next_channel(scan); - printk("common_hal_wifi_radio_start_scanning_networks done %p\n", scan); + LOG_DBG("common_hal_wifi_radio_start_scanning_networks done %p", scan); return scan; } void common_hal_wifi_radio_stop_scanning_networks(wifi_radio_obj_t *self) { - printk("common_hal_wifi_radio_stop_scanning_networks\n"); + LOG_DBG("common_hal_wifi_radio_stop_scanning_networks"); // Return early if self->current_scan is NULL to avoid hang if (self->current_scan == NULL) { return; @@ -456,17 +470,170 @@ wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t // // We're connected, allow us to retry if we get disconnected. // self->retries_left = self->starting_retries; // } + + struct wifi_connect_req_params params = { 0 }; + + params.ssid = ssid; + params.ssid_length = ssid_len; + params.band = WIFI_FREQ_BAND_2_4_GHZ; + params.channel = channel == 0 ? WIFI_CHANNEL_ANY : channel; + params.mfp = WIFI_MFP_OPTIONAL; + params.timeout = SYS_FOREVER_MS; + + if (password_len > 0) { + params.psk = password; + params.psk_length = password_len; + // WPA_AUTO_PERSONAL lets the driver settle on WPA2 or WPA3 with the AP + // rather than us guessing: it maps to WPA3-transition, which an AP in + // either mode accepts. WIFI_SECURITY_TYPE_UNKNOWN is not an option, the + // driver rejects it with -ENOTSUP. + params.security = WIFI_SECURITY_TYPE_WPA_AUTO_PERSONAL; + } else { + params.security = WIFI_SECURITY_TYPE_NONE; + } + + if (bssid_len == WIFI_MAC_ADDR_LEN) { + memcpy(params.bssid, bssid, WIFI_MAC_ADDR_LEN); + } + + // Already associated to the network being asked for: leave the link alone. + // supervisor_start_web_workflow() calls connect() on every invocation, so + // tearing the association down here would churn the link continuously. + if (self->connected && + ssid_len == self->current_ssid_len && + memcmp(ssid, self->current_ssid, ssid_len) == 0) { + return WIFI_RADIO_ERROR_NONE; + } + + // Switching networks. Connecting while associated returns -EALREADY and the + // failure path takes the interface down, so disconnect first. + if (self->connected) { + // A failure here is tolerated on purpose: if the interface really is + // unusable, the connect below returns a proper error to the caller. + (void)net_mgmt(NET_REQUEST_WIFI_DISCONNECT, self->sta_netif, NULL, 0); + // Give the controller a moment to tear the association down. + for (int i = 0; i < 40 && self->connected; i++) { + RUN_BACKGROUND_TASKS; + k_msleep(50); + } + self->connected = false; + } + + self->connected = false; + self->last_connect_status = -1; + self->last_disconnect_reason = 0; + k_sem_reset(&self->connect_sem); + + int res = net_mgmt(NET_REQUEST_WIFI_CONNECT, self->sta_netif, ¶ms, sizeof(params)); + if (res == -EALREADY) { + // Record the SSID as the success path does, so the early return above + // matches on a later connect() to the same network. + self->connected = true; + self->current_ssid_len = MIN(ssid_len, sizeof(self->current_ssid)); + memcpy(self->current_ssid, ssid, self->current_ssid_len); + return WIFI_RADIO_ERROR_NONE; + } + if (res < 0) { + return WIFI_RADIO_ERROR_UNSPECIFIED; + } + + // Wait for NET_EVENT_WIFI_CONNECT_RESULT (or a DISCONNECT_RESULT standing + // in for a failed attempt), staying responsive to ctrl-C. + mp_float_t timeout_s = timeout <= 0 ? (mp_float_t)10 : timeout; + int64_t deadline = k_uptime_get() + (int64_t)(timeout_s * 1000); + bool signalled = false; + while (k_uptime_get() < deadline) { + RUN_BACKGROUND_TASKS; + if (k_sem_take(&self->connect_sem, K_MSEC(50)) == 0) { + signalled = true; + break; + } + if (mp_hal_is_interrupted()) { + return WIFI_RADIO_ERROR_UNSPECIFIED; + } + } + + if (!signalled) { + return WIFI_RADIO_ERROR_HANDSHAKE_TIMEOUT; + } + if (!self->connected) { + switch (self->last_connect_status) { + case WIFI_STATUS_CONN_WRONG_PASSWORD: + return WIFI_RADIO_ERROR_AUTH_FAIL; + case WIFI_STATUS_CONN_AP_NOT_FOUND: + return WIFI_RADIO_ERROR_NO_AP_FOUND; + case WIFI_STATUS_CONN_TIMEOUT: + return WIFI_RADIO_ERROR_HANDSHAKE_TIMEOUT; + default: + return WIFI_RADIO_ERROR_CONNECTION_FAIL; + } + } + + // Remember which network this association is for, so a later connect() for + // the same SSID can return without disturbing it. + self->current_ssid_len = MIN(ssid_len, sizeof(self->current_ssid)); + memcpy(self->current_ssid, ssid, self->current_ssid_len); + + // Associated. Ask for an address; the AP side of DHCP can take a moment. + #if defined(CONFIG_NET_DHCPV4) + net_dhcpv4_start(self->sta_netif); + int64_t ip_deadline = k_uptime_get() + 15000; + while (k_uptime_get() < ip_deadline) { + if (net_if_ipv4_get_global_addr(self->sta_netif, NET_ADDR_PREFERRED) != NULL) { + break; + } + if (mp_hal_is_interrupted()) { + break; + } + RUN_BACKGROUND_TASKS; + k_msleep(50); + } + #endif + return WIFI_RADIO_ERROR_NONE; } bool common_hal_wifi_radio_get_connected(wifi_radio_obj_t *self) { - // return self->sta_mode && esp_netif_is_netif_up(self->netif); - return false; + return self->connected && self->sta_netif != NULL && + net_if_is_up(self->sta_netif); } mp_obj_t common_hal_wifi_radio_get_ap_info(wifi_radio_obj_t *self) { - // if (!esp_netif_is_netif_up(self->netif)) { - return mp_const_none; + if (self->sta_netif == NULL || !self->connected) { + return mp_const_none; + } + + // NET_REQUEST_WIFI_IFACE_STATUS carries everything a wifi.Network needs, so + // this reports the live association without spending a scan on it. + struct wifi_iface_status status = { 0 }; + if (net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, self->sta_netif, + &status, sizeof(status)) != 0) { + return mp_const_none; + } + + // Associated is the weakest state that has a meaningful BSSID and RSSI. + if (status.state < WIFI_STATE_ASSOCIATED) { + return mp_const_none; + } + + // wifi.Network wraps a scan result, so translate the status into one. + wifi_network_obj_t *ap_info = mp_obj_malloc(wifi_network_obj_t, &wifi_network_type); + size_t ssid_len = MIN(status.ssid_len, sizeof(ap_info->scan_result.ssid) - 1); + memcpy(ap_info->scan_result.ssid, status.ssid, ssid_len); + ap_info->scan_result.ssid[ssid_len] = '\0'; + ap_info->scan_result.ssid_length = ssid_len; + memcpy(ap_info->scan_result.mac, status.bssid, WIFI_MAC_ADDR_LEN); + ap_info->scan_result.mac_length = WIFI_MAC_ADDR_LEN; + ap_info->scan_result.band = status.band; + ap_info->scan_result.channel = status.channel; + ap_info->scan_result.security = status.security; + ap_info->scan_result.wpa3_ent_type = status.wpa3_ent_type; + ap_info->scan_result.mfp = status.mfp; + // status.rssi is int, scan_result.rssi is int8_t. Clamp, since a truncated + // value would wrap to a positive dBm. + ap_info->scan_result.rssi = (int8_t)MIN(MAX(status.rssi, INT8_MIN), INT8_MAX); + return MP_OBJ_FROM_PTR(ap_info); + // } // // Make sure the interface is in STA mode @@ -499,11 +666,23 @@ mp_obj_t common_hal_wifi_radio_get_ap_info(wifi_radio_obj_t *self) { } mp_obj_t common_hal_wifi_radio_get_ipv4_gateway(wifi_radio_obj_t *self) { - // if (!esp_netif_is_netif_up(self->netif)) { + if (self->sta_netif == NULL || !net_if_is_up(self->sta_netif)) { + return mp_const_none; + } + // net_if_ip.ipv4 only exists with CONFIG_NET_IPV4, and this file builds for + // every Wi-Fi board in the port, not just ones that enable it. + #if defined(CONFIG_NET_IPV4) + const struct net_if_config *cfg = net_if_get_config(self->sta_netif); + if (cfg == NULL || cfg->ip.ipv4 == NULL) { + return mp_const_none; + } + if (cfg->ip.ipv4->gw.s_addr == 0) { + return mp_const_none; + } + return common_hal_ipaddress_new_ipv4address(cfg->ip.ipv4->gw.s_addr); + #else return mp_const_none; - // } - // esp_netif_get_ip_info(self->netif, &self->ip_info); - // return common_hal_ipaddress_new_ipv4address(self->ip_info.gw.addr); + #endif } mp_obj_t common_hal_wifi_radio_get_ipv4_gateway_ap(wifi_radio_obj_t *self) { @@ -515,11 +694,24 @@ mp_obj_t common_hal_wifi_radio_get_ipv4_gateway_ap(wifi_radio_obj_t *self) { } mp_obj_t common_hal_wifi_radio_get_ipv4_subnet(wifi_radio_obj_t *self) { - // if (!esp_netif_is_netif_up(self->netif)) { + if (self->sta_netif == NULL || !net_if_is_up(self->sta_netif)) { + return mp_const_none; + } + // See get_ipv4_gateway: net_if_ip.ipv4 needs CONFIG_NET_IPV4. + #if defined(CONFIG_NET_IPV4) + struct net_if_ipv4 *ipv4 = self->sta_netif->config.ip.ipv4; + if (ipv4 == NULL) { + return mp_const_none; + } + for (int i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { + if (ipv4->unicast[i].ipv4.is_used && + ipv4->unicast[i].ipv4.addr_state == NET_ADDR_PREFERRED) { + return common_hal_ipaddress_new_ipv4address( + ipv4->unicast[i].netmask.s_addr); + } + } + #endif return mp_const_none; - // } - // esp_netif_get_ip_info(self->netif, &self->ip_info); - // return common_hal_ipaddress_new_ipv4address(self->ip_info.netmask.addr); } mp_obj_t common_hal_wifi_radio_get_ipv4_subnet_ap(wifi_radio_obj_t *self) { @@ -562,31 +754,48 @@ mp_obj_t common_hal_wifi_radio_get_ipv4_subnet_ap(wifi_radio_obj_t *self) { // } mp_obj_t common_hal_wifi_radio_get_addresses(wifi_radio_obj_t *self) { - // return common_hal_wifi_radio_get_addresses_netif(self, self->netif); - return mp_const_none; + // shared-bindings documents this as Sequence[str], empty when not + // connected, so format as a dotted-quad string rather than returning an + // IPv4Address object. Same address as wifi_radio_get_ipv4_address(). + uint32_t ipv4_address = wifi_radio_get_ipv4_address(self); + if (ipv4_address == 0) { + return mp_const_empty_tuple; + } + uint8_t *octets = (uint8_t *)&ipv4_address; + char buf[16]; + snprintf(buf, sizeof(buf), "%d.%d.%d.%d", octets[0], octets[1], octets[2], octets[3]); + mp_obj_t args[] = { mp_obj_new_str(buf, strlen(buf)) }; + return mp_obj_new_tuple(MP_ARRAY_SIZE(args), args); } mp_obj_t common_hal_wifi_radio_get_addresses_ap(wifi_radio_obj_t *self) { - // return common_hal_wifi_radio_get_addresses_netif(self, self->ap_netif); - return mp_const_none; + // AP mode is unimplemented here, but mp_const_none is still the wrong type + // for the Sequence[str] contract. + return mp_const_empty_tuple; } uint32_t wifi_radio_get_ipv4_address(wifi_radio_obj_t *self) { - // if (!esp_netif_is_netif_up(self->netif)) { - // return 0; - // } - // esp_netif_get_ip_info(self->netif, &self->ip_info); - // return self->ip_info.ip.addr; - return 0; + // Raw uint32_t sibling of common_hal_wifi_radio_get_ipv4_address(), + // used internally by supervisor/shared/web_workflow/web_workflow.c. + if (self->sta_netif == NULL || !net_if_is_up(self->sta_netif)) { + return 0; + } + struct in_addr *addr = net_if_ipv4_get_global_addr(self->sta_netif, NET_ADDR_PREFERRED); + if (addr == NULL) { + return 0; + } + return addr->s_addr; } mp_obj_t common_hal_wifi_radio_get_ipv4_address(wifi_radio_obj_t *self) { - // if (!esp_netif_is_netif_up(self->netif)) { - // return mp_const_none; - // } - // esp_netif_get_ip_info(self->netif, &self->ip_info); - // return common_hal_ipaddress_new_ipv4address(self->ip_info.ip.addr); - return mp_const_none; + if (self->sta_netif == NULL || !net_if_is_up(self->sta_netif)) { + return mp_const_none; + } + struct in_addr *addr = net_if_ipv4_get_global_addr(self->sta_netif, NET_ADDR_PREFERRED); + if (addr == NULL) { + return mp_const_none; + } + return common_hal_ipaddress_new_ipv4address(addr->s_addr); } mp_obj_t common_hal_wifi_radio_get_ipv4_address_ap(wifi_radio_obj_t *self) { @@ -599,20 +808,26 @@ mp_obj_t common_hal_wifi_radio_get_ipv4_address_ap(wifi_radio_obj_t *self) { } mp_obj_t common_hal_wifi_radio_get_ipv4_dns(wifi_radio_obj_t *self) { - // if (!esp_netif_is_netif_up(self->netif)) { - // return mp_const_none; - // } - - // esp_netif_get_dns_info(self->netif, ESP_NETIF_DNS_MAIN, &self->dns_info); - - // if (self->dns_info.ip.type != ESP_IPADDR_TYPE_V4) { - // return mp_const_none; - // } - // // dns_info is of type esp_netif_dns_info_t, which is just ever so slightly - // // different than esp_netif_ip_info_t used for - // // common_hal_wifi_radio_get_ipv4_address (includes both ipv4 and 6), - // // so some extra jumping is required to get to the actual address - // return common_hal_ipaddress_new_ipv4address(self->dns_info.ip.u_addr.ip4.addr); + // Zephyr keeps resolver state in the DNS resolve context rather than on + // the interface, so read it there. + #if defined(CONFIG_DNS_RESOLVER) + if (self->sta_netif == NULL || !net_if_is_up(self->sta_netif)) { + return mp_const_none; + } + struct dns_resolve_context *ctx = dns_resolve_get_default(); + if (ctx == NULL) { + return mp_const_none; + } + for (int i = 0; i < CONFIG_DNS_RESOLVER_MAX_SERVERS; i++) { + if (ctx->servers[i].dns_server.sa_family == AF_INET) { + struct sockaddr_in *addr = + (struct sockaddr_in *)&ctx->servers[i].dns_server; + if (addr->sin_addr.s_addr != 0) { + return common_hal_ipaddress_new_ipv4address(addr->sin_addr.s_addr); + } + } + } + #endif return mp_const_none; } @@ -685,58 +900,187 @@ void common_hal_wifi_radio_set_ipv4_address_ap(wifi_radio_obj_t *self, mp_obj_t // common_hal_wifi_radio_start_dhcp_server(self); // restart access point DHCP } -// static void ping_success_cb(esp_ping_handle_t hdl, void *args) { -// wifi_radio_obj_t *self = (wifi_radio_obj_t *)args; -// esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &self->ping_elapsed_time, sizeof(self->ping_elapsed_time)); -// } +#if CIRCUITPY_WIFI_PING +// Zephyr delivers the echo reply on the network RX thread, not the caller's, so +// the two halves share this state through the ICMP context's user_data. It lives +// on the calling thread's stack, hence the cleanup discipline in ping() below. +typedef struct { + struct k_sem reply_sem; + int64_t sent_ms; + int64_t elapsed_ms; + uint16_t identifier; + uint16_t sequence; +} ping_session_t; + +// Zephyr keeps struct net_icmpv4_echo_req in subsys/net/ip/icmpv4.h, a private +// header that code outside the net stack cannot include, so mirror the four +// bytes that follow the ICMP header here. +struct ping_echo_hdr { + uint16_t identifier; + uint16_t sequence; +} __packed; + +// An echo carries no port number, so the sequence is the only thing telling two +// back-to-back requests apart; without it a late reply would be reported as the +// next call's round trip. The identifier is randomized once per boot. +static uint16_t ping_identifier; +static uint16_t ping_sequence; + +static enum net_verdict ping_reply_handler(struct net_icmp_ctx *ctx, + struct net_pkt *pkt, + struct net_icmp_ip_hdr *ip_hdr, + struct net_icmp_hdr *icmp_hdr, + void *user_data) { + NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(echo_access, struct ping_echo_hdr); + ping_session_t *session = user_data; + struct ping_echo_hdr *echo; + + (void)ctx; + (void)ip_hdr; + (void)icmp_hdr; + + if (session == NULL) { + return NET_CONTINUE; + } + + // net_pkt_get_data() leaves the cursor where it found it, which the + // NET_CONTINUE below relies on: the next handler starts at the echo header. + echo = (struct ping_echo_hdr *)net_pkt_get_data(pkt, &echo_access); + if (echo == NULL) { + return NET_CONTINUE; + } + + if (net_ntohs(echo->identifier) != session->identifier || + net_ntohs(echo->sequence) != session->sequence) { + // A reply to an earlier ping of ours, or to somebody else's. Leave it + // alone rather than waking the caller with a round trip time that + // belongs to a different request. + return NET_CONTINUE; + } + + // Stamp arrival here rather than after k_sem_take() returns, so the + // measurement does not absorb the woken thread's scheduling delay. + session->elapsed_ms = k_uptime_get() - session->sent_ms; + k_sem_give(&session->reply_sem); + + return NET_OK; +} mp_int_t common_hal_wifi_radio_ping(wifi_radio_obj_t *self, mp_obj_t ip_address, mp_float_t timeout) { - // esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG(); - // ipaddress_ipaddress_to_esp_idf(ip_address, &ping_config.target_addr); - // ping_config.count = 1; - - // // We must fetch ping information using the callback mechanism, because the session storage is freed when - // // the ping session is done, even before esp_ping_delete_session(). - // esp_ping_callbacks_t ping_callbacks = { - // .on_ping_success = ping_success_cb, - // .cb_args = (void *)self, - // }; + // radio.ping() is documented to take an ipaddress.IPv4Address. + if (!mp_obj_is_type(ip_address, &ipaddress_ipv4address_type)) { + mp_raise_ValueError(MP_ERROR_TEXT("Only IPv4 addresses supported")); + } - // size_t timeout_ms = timeout * 1000; + // get_packed() takes a concrete ipaddress_ipv4address_obj_t *, so the + // MP_OBJ_TO_PTR is needed under object representations C and D. + ipaddress_ipv4address_obj_t *addr_obj = MP_OBJ_TO_PTR(ip_address); + size_t packed_len; + const char *packed = mp_obj_str_get_data( + common_hal_ipaddress_ipv4address_get_packed(addr_obj), &packed_len); + if (packed_len != sizeof(struct net_in_addr)) { + mp_raise_ValueError(MP_ERROR_TEXT("Only IPv4 addresses supported")); + } - // // ESP-IDF creates a task to do the ping session. It shuts down when done, but only after a one second delay. - // // Calling common_hal_wifi_radio_ping() too fast will cause resource exhaustion. - // esp_ping_handle_t ping; - // if (esp_ping_new_session(&ping_config, &ping_callbacks, &ping) != ESP_OK) { - // // Wait for old task to go away and then try again. - // // Empirical testing shows we have to wait at least two seconds, despite the task - // // having a one-second timeout. - // common_hal_time_delay_ms(2000); - // // Return if interrupted now, to show the interruption as KeyboardInterrupt instead of the - // // IDF error. - // if (mp_hal_is_interrupted()) { - // return (uint32_t)(-1); - // } - // CHECK_ESP_RESULT(esp_ping_new_session(&ping_config, &ping_callbacks, &ping)); - // } + // This Zephyr renamed the socket address types, so the destination is a + // struct net_sockaddr_in carrying NET_AF_INET, not a sockaddr_in/AF_INET. + struct net_sockaddr_in dst = { + .sin_family = NET_AF_INET, + }; + memcpy(&dst.sin_addr, packed, sizeof(dst.sin_addr)); + + if (ping_identifier == 0) { + // Seeded lazily. sys_rand16_get() may legitimately return 0, in which + // case we simply reseed on the next call. + ping_identifier = sys_rand16_get(); + } - // // Use all ones as a flag that the elapsed time was not set (ping failed or timed out). - // self->ping_elapsed_time = (uint32_t)(-1); + ping_session_t session = { + .identifier = ping_identifier, + .sequence = ++ping_sequence, + .elapsed_ms = -1, + }; + k_sem_init(&session.reply_sem, 0, 1); + + struct net_icmp_ctx icmp_ctx; + int res = net_icmp_init_ctx(&icmp_ctx, NET_AF_INET, NET_ICMPV4_ECHO_REPLY, 0, + ping_reply_handler); + if (res < 0) { + LOG_DBG("ping: net_icmp_init_ctx failed (%d)", res); + return -1; + } - // esp_ping_start(ping); + struct net_icmp_ping_params params = { + .identifier = session.identifier, + .sequence = session.sequence, + .tc_tos = 0, + // A negative priority leaves the packet at the stack default and lets + // tc_tos drive the DSCP/ECN bits instead. + .priority = -1, + .data = NULL, + .data_size = 0, + }; + + session.sent_ms = k_uptime_get(); + + // A NULL sta_netif is fine; the stack picks an interface from the + // destination. This is the blocking send, as Zephyr's net shell uses: it + // waits up to a second for a buffer, so a ping can take timeout + 1s. + res = net_icmp_send_echo_request(&icmp_ctx, self->sta_netif, + (struct net_sockaddr *)&dst, ¶ms, &session); + if (res < 0) { + LOG_DBG("ping: send failed (%d)", res); + (void)net_icmp_cleanup_ctx(&icmp_ctx); + return -1; + } - // uint32_t start_time = common_hal_time_monotonic_ms(); - // while ((self->ping_elapsed_time == (uint32_t)(-1)) && - // (common_hal_time_monotonic_ms() - start_time < timeout_ms) && - // !mp_hal_is_interrupted()) { - // RUN_BACKGROUND_TASKS; - // } - // esp_ping_stop(ping); - // esp_ping_delete_session(ping); + // Wait for ping_reply_handler() to signal, staying responsive to ctrl-C at + // the same 50 ms granularity as the association wait in + // common_hal_wifi_radio_connect(). + mp_float_t timeout_s = timeout <= 0 ? (mp_float_t)0.5 : timeout; + int64_t deadline = k_uptime_get() + (int64_t)(timeout_s * 1000); + bool replied = false; + while (true) { + int64_t remaining_ms = deadline - k_uptime_get(); + if (remaining_ms <= 0) { + break; + } + + // Poll in 50 ms slices so ctrl-C stays responsive, clamped to the + // caller's deadline so a reply arriving after the timeout is not + // reported as a success. MIN() is avoided here because py/misc.h and + // zephyr/sys/util.h both define it and this file includes both. + int64_t wait_ms = remaining_ms < 50 ? remaining_ms : 50; + + if (k_sem_take(&session.reply_sem, K_MSEC(wait_ms)) == 0) { + replied = true; + break; + } + if (mp_hal_is_interrupted()) { + break; + } + } - // return (mp_int_t)self->ping_elapsed_time; - return 0; + // Unregister before returning. net_icmp_cleanup_ctx() takes the same mutex + // the stack holds while dispatching handlers, so once it returns no handler + // can still be looking at session, which lives on this stack frame. This + // has to happen on every exit path, including the error paths above. + (void)net_icmp_cleanup_ctx(&icmp_ctx); + + if (!replied || session.elapsed_ms < 0) { + return -1; + } + + // shared-bindings turns exactly -1 into None and divides anything else by + // 1000, so every failure path must return -1, not 0. + return (mp_int_t)session.elapsed_ms; } +#else +mp_int_t common_hal_wifi_radio_ping(wifi_radio_obj_t *self, mp_obj_t ip_address, mp_float_t timeout) { + // Boards that turn ping off to save flash report no reply. + return -1; +} +#endif void common_hal_wifi_radio_gc_collect(wifi_radio_obj_t *self) { // Only bother to scan the actual object references. diff --git a/ports/zephyr-cp/common-hal/wifi/Radio.h b/ports/zephyr-cp/common-hal/wifi/Radio.h index f177f493685..2500079df09 100644 --- a/ports/zephyr-cp/common-hal/wifi/Radio.h +++ b/ports/zephyr-cp/common-hal/wifi/Radio.h @@ -11,7 +11,9 @@ #include "shared-bindings/wifi/ScannedNetworks.h" #include "shared-bindings/wifi/Network.h" +#include #include +#include // Event bits for the Radio event group. #define WIFI_SCAN_DONE_BIT BIT0 @@ -38,6 +40,16 @@ typedef struct { uint8_t retries_left; uint8_t starting_retries; uint8_t last_disconnect_reason; + // Signalled from the net_mgmt event handler when a connect attempt + // finishes, so common_hal_wifi_radio_connect() can wait on the result. + struct k_sem connect_sem; + // Latest wifi_conn_status from NET_EVENT_WIFI_CONNECT_RESULT. + int last_connect_status; + bool connected; + // SSID of the association that `connected` refers to, so that a connect() + // for the network we are already on can return without touching the link. + uint8_t current_ssid[WIFI_SSID_MAX_LEN]; + size_t current_ssid_len; } wifi_radio_obj_t; extern void common_hal_wifi_radio_gc_collect(wifi_radio_obj_t *self); diff --git a/ports/zephyr-cp/common-hal/wifi/ScannedNetworks.c b/ports/zephyr-cp/common-hal/wifi/ScannedNetworks.c index 725bf1fa7cb..d7df14ffa5c 100644 --- a/ports/zephyr-cp/common-hal/wifi/ScannedNetworks.c +++ b/ports/zephyr-cp/common-hal/wifi/ScannedNetworks.c @@ -19,12 +19,15 @@ #include "bindings/zephyr_kernel/__init__.h" #include +#include #include +LOG_MODULE_DECLARE(cp_wifi); + void wifi_scannednetworks_scan_result(wifi_scannednetworks_obj_t *self, struct wifi_scan_result *result) { if (k_msgq_put(&self->msgq, result, K_NO_WAIT) != 0) { - printk("Dropping scan result!\n"); + LOG_WRN("Dropping scan result"); } } @@ -104,7 +107,7 @@ void wifi_scannednetworks_scan_next_channel(wifi_scannednetworks_obj_t *self) { } else { int res = net_mgmt(NET_REQUEST_WIFI_SCAN, self->netif, ¶ms, sizeof(params)); if (res != 0) { - printk("Failed to start wifi scan %d\n", res); + LOG_ERR("Failed to start wifi scan %d", res); raise_zephyr_error(res); wifi_scannednetworks_done(self); } else { diff --git a/ports/zephyr-cp/common-hal/wifi/__init__.c b/ports/zephyr-cp/common-hal/wifi/__init__.c index 4b967bc2780..182ae5a3c30 100644 --- a/ports/zephyr-cp/common-hal/wifi/__init__.c +++ b/ports/zephyr-cp/common-hal/wifi/__init__.c @@ -31,12 +31,15 @@ wifi_radio_obj_t common_hal_wifi_radio_obj; #endif #include +#include #include #include #define MAC_ADDRESS_LENGTH 6 +LOG_MODULE_REGISTER(cp_wifi, CONFIG_LOG_DEFAULT_LEVEL); + static void schedule_background_on_cp_core(void *arg) { #if CIRCUITPY_STATUS_BAR supervisor_status_bar_request_update(false); @@ -56,7 +59,7 @@ static void _event_handler(struct net_mgmt_event_callback *cb, uint64_t mgmt_eve switch (mgmt_event) { case NET_EVENT_WIFI_SCAN_RESULT: { - printk("NET_EVENT_WIFI_SCAN_RESULT\n"); + LOG_DBG("NET_EVENT_WIFI_SCAN_RESULT"); const struct wifi_scan_result *result = cb->info; if (result != NULL && self->current_scan != NULL) { wifi_scannednetworks_scan_result(self->current_scan, result); @@ -64,54 +67,75 @@ static void _event_handler(struct net_mgmt_event_callback *cb, uint64_t mgmt_eve break; } case NET_EVENT_WIFI_SCAN_DONE: - printk("NET_EVENT_WIFI_SCAN_DONE (thread: %s prio=%d)\n", + LOG_DBG("NET_EVENT_WIFI_SCAN_DONE (thread: %s prio=%d)", k_thread_name_get(k_current_get()), k_thread_priority_get(k_current_get())); if (self->current_scan != NULL) { k_poll_signal_raise(&self->current_scan->channel_done, 0); } break; - case NET_EVENT_WIFI_CONNECT_RESULT: - printk("NET_EVENT_WIFI_CONNECT_RESULT\n"); + case NET_EVENT_WIFI_CONNECT_RESULT: { + const struct wifi_status *status = cb->info; + self->last_connect_status = status != NULL ? status->status : -1; + self->connected = self->last_connect_status == WIFI_STATUS_CONN_SUCCESS; + LOG_DBG("NET_EVENT_WIFI_CONNECT_RESULT status %d", self->last_connect_status); + k_sem_give(&self->connect_sem); break; - case NET_EVENT_WIFI_DISCONNECT_RESULT: - printk("NET_EVENT_WIFI_DISCONNECT_RESULT\n"); + } + case NET_EVENT_WIFI_DISCONNECT_RESULT: { + const struct wifi_status *status = cb->info; + self->last_disconnect_reason = status != NULL ? (uint8_t)status->status : 0; + self->connected = false; + LOG_DBG("NET_EVENT_WIFI_DISCONNECT_RESULT reason %d", self->last_disconnect_reason); + // A disconnect can also be the failure result of a connect attempt, + // so release any waiter rather than letting it sit until timeout. + k_sem_give(&self->connect_sem); break; + } case NET_EVENT_WIFI_IFACE_STATUS: - printk("NET_EVENT_WIFI_IFACE_STATUS\n"); + LOG_DBG("NET_EVENT_WIFI_IFACE_STATUS"); break; case NET_EVENT_WIFI_TWT: - printk("NET_EVENT_WIFI_TWT\n"); + LOG_DBG("NET_EVENT_WIFI_TWT"); break; case NET_EVENT_WIFI_TWT_SLEEP_STATE: - printk("NET_EVENT_WIFI_TWT_SLEEP_STATE\n"); + LOG_DBG("NET_EVENT_WIFI_TWT_SLEEP_STATE"); break; case NET_EVENT_WIFI_RAW_SCAN_RESULT: - printk("NET_EVENT_WIFI_RAW_SCAN_RESULT\n"); + LOG_DBG("NET_EVENT_WIFI_RAW_SCAN_RESULT"); break; case NET_EVENT_WIFI_DISCONNECT_COMPLETE: - printk("NET_EVENT_WIFI_DISCONNECT_COMPLETE\n"); + LOG_DBG("NET_EVENT_WIFI_DISCONNECT_COMPLETE"); break; case NET_EVENT_WIFI_SIGNAL_CHANGE: - printk("NET_EVENT_WIFI_SIGNAL_CHANGE\n"); + LOG_DBG("NET_EVENT_WIFI_SIGNAL_CHANGE"); break; case NET_EVENT_WIFI_NEIGHBOR_REP_COMP: - printk("NET_EVENT_WIFI_NEIGHBOR_REP_COMP\n"); + LOG_DBG("NET_EVENT_WIFI_NEIGHBOR_REP_COMP"); break; case NET_EVENT_WIFI_AP_ENABLE_RESULT: - printk("NET_EVENT_WIFI_AP_ENABLE_RESULT\n"); + LOG_DBG("NET_EVENT_WIFI_AP_ENABLE_RESULT"); break; case NET_EVENT_WIFI_AP_DISABLE_RESULT: - printk("NET_EVENT_WIFI_AP_DISABLE_RESULT\n"); + LOG_DBG("NET_EVENT_WIFI_AP_DISABLE_RESULT"); break; case NET_EVENT_WIFI_AP_STA_CONNECTED: - printk("NET_EVENT_WIFI_AP_STA_CONNECTED\n"); + LOG_DBG("NET_EVENT_WIFI_AP_STA_CONNECTED"); break; case NET_EVENT_WIFI_AP_STA_DISCONNECTED: - printk("NET_EVENT_WIFI_AP_STA_DISCONNECTED\n"); + LOG_DBG("NET_EVENT_WIFI_AP_STA_DISCONNECTED"); + break; + case NET_EVENT_IPV4_ADDR_ADD: + // DHCP bound, or a static address was configured. The address is read + // live by the ipv4_address getter, so nothing is stored here; the + // status bar just needs a refresh or it keeps showing "No IP". + LOG_DBG("NET_EVENT_IPV4_ADDR_ADD"); + schedule_background_on_cp_core(NULL); break; default: - printk("unhandled net event %x\n", mgmt_event); + // Print all 64 bits. The layer lives in the high bits, so a 32-bit + // print collapses every unhandled event in a layer to one value. + LOG_DBG("unhandled net event %llx", (unsigned long long)mgmt_event); break; } } @@ -196,7 +220,7 @@ static bool wifi_user_initiated; void common_hal_wifi_init(bool user_initiated) { wifi_radio_obj_t *self = &common_hal_wifi_radio_obj; - printk("common_hal_wifi_init\n"); + LOG_DBG("common_hal_wifi_init"); if (wifi_inited) { if (user_initiated && !wifi_user_initiated) { @@ -207,6 +231,9 @@ void common_hal_wifi_init(bool user_initiated) { wifi_inited = true; wifi_user_initiated = user_initiated; self->base.type = &wifi_radio_type; + k_sem_init(&self->connect_sem, 0, 1); + self->connected = false; + self->last_connect_status = -1; // struct net_if *default_iface = net_if_get_default(); // printk("default interface %p\n", default_iface); @@ -223,8 +250,8 @@ void common_hal_wifi_init(bool user_initiated) { // } self->sta_netif = net_if_get_wifi_sta(); self->ap_netif = net_if_get_wifi_sap(); - printk("sta_netif %p\n", self->sta_netif); - printk("ap_netif %p\n", self->ap_netif); + LOG_DBG("sta_netif %p", self->sta_netif); + LOG_DBG("ap_netif %p", self->ap_netif); struct wifi_iface_status status = { 0 }; @@ -232,39 +259,39 @@ void common_hal_wifi_init(bool user_initiated) { CHECK_ZEPHYR_RESULT(net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, self->sta_netif, &status, sizeof(struct wifi_iface_status))); if (net_if_is_up(self->sta_netif)) { - printk("STA is up\n"); + LOG_DBG("STA is up"); } else { - printk("STA is down\n"); + LOG_DBG("STA is down"); } if (net_if_is_carrier_ok(self->sta_netif)) { - printk("STA carrier is ok\n"); + LOG_DBG("STA carrier is ok"); } else { - printk("STA carrier is not ok\n"); + LOG_DBG("STA carrier is not ok"); } if (net_if_is_dormant(self->sta_netif)) { - printk("STA is dormant\n"); + LOG_DBG("STA is dormant"); } else { - printk("STA is not dormant\n"); + LOG_DBG("STA is not dormant"); } } if (self->ap_netif != NULL) { int res = net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, self->ap_netif, &status, sizeof(struct wifi_iface_status)); - printk("AP status request response %d\n", res); + LOG_DBG("AP status request response %d", res); if (net_if_is_up(self->ap_netif)) { - printk("AP is up\n"); + LOG_DBG("AP is up"); } else { - printk("AP is down\n"); + LOG_DBG("AP is down"); } if (net_if_is_carrier_ok(self->ap_netif)) { - printk("AP carrier is ok\n"); + LOG_DBG("AP carrier is ok"); } else { - printk("AP carrier is not ok\n"); + LOG_DBG("AP carrier is not ok"); } if (net_if_is_dormant(self->ap_netif)) { - printk("AP is dormant\n"); + LOG_DBG("AP is dormant"); } else { - printk("AP is not dormant\n"); + LOG_DBG("AP is not dormant"); } } @@ -278,6 +305,7 @@ void common_hal_wifi_init(bool user_initiated) { // self->ap_mode = 0; net_mgmt_init_event_callback(&wifi_cb, _event_handler, + NET_EVENT_WIFI_SCAN_RESULT | NET_EVENT_WIFI_SCAN_DONE | NET_EVENT_WIFI_CONNECT_RESULT | NET_EVENT_WIFI_DISCONNECT_RESULT | @@ -317,21 +345,21 @@ void common_hal_wifi_init(bool user_initiated) { char cpy_default_hostname[board_len + (MAC_ADDRESS_LENGTH * 2) + 6]; struct net_linkaddr *mac = net_if_get_link_addr(self->sta_netif); if (mac->len < MAC_ADDRESS_LENGTH) { - printk("MAC address too short"); + LOG_ERR("MAC address too short"); } snprintf(cpy_default_hostname, sizeof(cpy_default_hostname), "cpy-%s-%02x%02x%02x%02x%02x%02x", CIRCUITPY_BOARD_ID + board_trim, mac->addr[0], mac->addr[1], mac->addr[2], mac->addr[3], mac->addr[4], mac->addr[5]); CHECK_ZEPHYR_RESULT(net_hostname_set(cpy_default_hostname, strlen(cpy_default_hostname))); } #else - printk("Hostname support disabled in Zephyr config\n"); + LOG_WRN("Hostname support disabled in Zephyr config"); #endif // set station mode to avoid the default SoftAP common_hal_wifi_radio_start_station(self); // start wifi common_hal_wifi_radio_set_enabled(self, true); - printk("common_hal_wifi_init done\n"); + LOG_DBG("common_hal_wifi_init done"); } void wifi_user_reset(void) { @@ -342,7 +370,7 @@ void wifi_user_reset(void) { } void wifi_reset(void) { - printk("wifi_reset\n"); + LOG_DBG("wifi_reset"); if (!wifi_inited) { return; } diff --git a/ports/zephyr-cp/common-hal/wifi/__init__.h b/ports/zephyr-cp/common-hal/wifi/__init__.h index dab519b1a5f..937ac699021 100644 --- a/ports/zephyr-cp/common-hal/wifi/__init__.h +++ b/ports/zephyr-cp/common-hal/wifi/__init__.h @@ -8,6 +8,8 @@ #include "py/obj.h" +#include + struct sockaddr_storage; void wifi_reset(void); diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index b7334402b74..cb36ce7f3da 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -67,9 +67,17 @@ "adafruit_bus_device", "getpass", "storage", + "binascii", + "re", + "asyncio", + "select", ] # Flags that don't match with with a *bindings module. Some used by adafruit_requests -MPCONFIG_FLAGS = ["array", "errno", "io", "json", "math"] +MPCONFIG_FLAGS = ["array", "errno", "io", "json", "math", "binascii"] + +# extmod-based modules that should appear in the autogen list even though they +# don't have shared-bindings/ or bindings/ directories. +EXTMOD_MODULES = ["asyncio", "binascii", "json", "re", "select"] # List of other modules (the value) that can be enabled when another one (the key) is. REVERSE_DEPENDENCIES = { @@ -343,7 +351,7 @@ def determine_enabled_modules(board_info, portdir, srcdir): return enabled_modules, module_reasons -async def build_circuitpython(): +async def build_circuitpython(): # noqa: C901 circuitpython_flags = ["-DCIRCUITPY"] port_flags = [] enable_mpy_native = False @@ -361,7 +369,13 @@ async def build_circuitpython(): lto = cmake_args.get("LTO", "n") == "y" circuitpython_flags.append(f"-DCIRCUITPY_ENABLE_MPY_NATIVE={1 if enable_mpy_native else 0}") circuitpython_flags.append(f"-DCIRCUITPY_FULL_BUILD={1 if full_build else 0}") + circuitpython_flags.append("-DCIRCUITPY_OPT_LOAD_ATTR_FAST_PATH=1") + circuitpython_flags.append(f"-DCIRCUITPY_OPT_MAP_LOOKUP_CACHE={1 if full_build else 0}") circuitpython_flags.append(f"-DCIRCUITPY_SETTINGS_TOML={1 if full_build else 0}") + circuitpython_flags.append(f"-DMICROPY_PY_ASYNC_AWAIT={1 if full_build else 0}") + circuitpython_flags.append(f"-DMICROPY_PY_ASYNCIO={1 if full_build else 0}") + circuitpython_flags.append(f"-DMICROPY_PY_SELECT={1 if full_build else 0}") + circuitpython_flags.append(f"-DMICROPY_PY_SELECT_SELECT={1 if full_build else 0}") circuitpython_flags.append("-DCIRCUITPY_STATUS_BAR=1") circuitpython_flags.append(f"-DCIRCUITPY_USB_HOST={1 if usb_host else 0}") circuitpython_flags.append(f"-DCIRCUITPY_BOARD_ID='\"{board}\"'") @@ -434,6 +448,10 @@ async def build_circuitpython(): supervisor_source = [ "main.c", "extmod/modjson.c", + "extmod/modbinascii.c", + "extmod/modre.c", + "extmod/modasyncio.c", + "extmod/modselect.c", "extmod/vfs_fat.c", "lib/tlsf/tlsf.c", portdir / "background.c", @@ -463,8 +481,16 @@ async def build_circuitpython(): supervisor_source = [pathlib.Path(p) for p in supervisor_source] supervisor_source.extend(board_info["source_files"]) supervisor_source.extend(top.glob("supervisor/shared/*.c")) - if "_bleio" in enabled_modules: + ble_workflow_enabled = "_bleio" in enabled_modules + if ble_workflow_enabled: supervisor_source.append(top / "supervisor/shared/bluetooth/bluetooth.c") + # BLE workflow = file transfer + serial services, matching other ports. + supervisor_source.append(top / "supervisor/shared/bluetooth/file_transfer.c") + supervisor_source.append(top / "supervisor/shared/bluetooth/serial.c") + circuitpython_flags.append(f"-DCIRCUITPY_BLE_FILE_SERVICE={1 if ble_workflow_enabled else 0}") + circuitpython_flags.append( + f"-DCIRCUITPY_BLE_SERIAL_SERVICE={1 if ble_workflow_enabled else 0}" + ) supervisor_source.append(top / "supervisor/shared/translate/translate.c") if web_workflow_enabled: supervisor_source.extend(top.glob("supervisor/shared/web_workflow/*.c")) @@ -474,6 +500,11 @@ async def build_circuitpython(): if usb_ok: enabled_modules.add("usb_cdc") + enabled_modules.add("usb_hid") + + usb_num_endpoint_pairs = board_info.get("usb_num_endpoint_pairs", 4) + circuitpython_flags.append(f"-DUSB_NUM_ENDPOINT_PAIRS={usb_num_endpoint_pairs}") + circuitpython_flags.append("-DCIRCUITPY_USB_HID_ENABLED_DEFAULT=1") for macro in ("USB_PID", "USB_VID"): print(f"Setting {macro} to {mpconfigboard.get(macro)}") @@ -578,6 +609,10 @@ async def build_circuitpython(): # Only include shared-module/*.c if no common-hal/*.c files were found if len(hal_source) == len_before or module.name in SHARED_MODULE_AND_COMMON_HAL: hal_source.extend(top.glob(f"shared-module/{module.name}/**/*.c")) + # The USB HID report descriptors are shared with the TinyUSB ports and + # have no common-hal override, so always compile them. + if module.name == "usb_hid": + hal_source.append(top / "shared-module/usb_hid/report_descriptors.c") hal_source.extend(top.glob(f"shared-bindings/{module.name}/**/*.c")) if module.name in LIBRARY_SOURCE: for library_source in LIBRARY_SOURCE[module.name]: @@ -592,6 +627,16 @@ async def build_circuitpython(): logger.warning( f"autogen_board_info.toml is missing or out of date. Please run `make BOARD={board}` locally and commit {autogen_board_info_fn}." ) + autogen_modules.add(tomlkit.comment("extmod modules shared with MicroPython")) + for extmod_module in EXTMOD_MODULES: + enabled = extmod_module in enabled_modules + v = tomlkit.item(enabled) + if extmod_module in module_reasons: + v.comment(module_reasons[extmod_module]) + autogen_modules.add(extmod_module, v) + flag_name = MODULE_FLAG_NAMES.get(extmod_module, extmod_module.upper()) + circuitpython_flags.append(f"-DCIRCUITPY_{flag_name}={1 if enabled else 0}") + if autogen_board_info_fn.parent.exists(): autogen_board_info_fn.write_text(tomlkit.dumps(autogen_board_info)) @@ -599,7 +644,29 @@ async def build_circuitpython(): enabled = mpflag in DEFAULT_MODULES circuitpython_flags.append(f"-DCIRCUITPY_{mpflag.upper()}={1 if enabled else 0}") + # ulab is on by default and boards that cannot spare the flash set + # CIRCUITPY_ULAB = false in their circuitpython.toml. Flags mirror py/py.mk. + ulab_enabled = mpconfigboard.get("CIRCUITPY_ULAB", True) + circuitpython_flags.append(f"-DCIRCUITPY_ULAB={1 if ulab_enabled else 0}") + if ulab_enabled: + circuitpython_flags.extend( + ( + "-DMODULE_ULAB_ENABLED=1", + "-DULAB_HAS_USER_MODULE=0", + "-iquote", + str(top / "extmod" / "ulab" / "code"), + ) + ) + # radio.ping() builds on Zephyr's ICMP API. On by default; boards that cannot + # spare the flash set CIRCUITPY_WIFI_PING = false in their circuitpython.toml + # and radio.ping() then reports no reply, as it does for an unreachable host. + circuitpython_flags.append( + f"-DCIRCUITPY_WIFI_PING={1 if mpconfigboard.get('CIRCUITPY_WIFI_PING', True) else 0}" + ) + source_files = supervisor_source + hal_source + ["extmod/vfs.c"] + if ulab_enabled: + source_files.extend(sorted((top / "extmod" / "ulab" / "code").rglob("*.c"))) assembly_files = [] for file in top.glob("py/*.c"): source_files.append(file) diff --git a/ports/zephyr-cp/cptools/compat2driver.py b/ports/zephyr-cp/cptools/compat2driver.py index 7da74ffcf18..8cd3c248265 100644 --- a/ports/zephyr-cp/cptools/compat2driver.py +++ b/ports/zephyr-cp/cptools/compat2driver.py @@ -183,6 +183,7 @@ "zephyr_bt_hci_spi": "bluetooth/hci", "zephyr_bt_hci_uart": "bluetooth/hci", "zephyr_bt_hci_userchan": "bluetooth/hci", + "zephyr_bt_hci_ll_sw_split": "bluetooth/hci", # # cache "bflb_l1c": "cache", diff --git a/ports/zephyr-cp/cptools/zephyr2cp.py b/ports/zephyr-cp/cptools/zephyr2cp.py index a9b5ecf6e1d..cc40ccfda85 100644 --- a/ports/zephyr-cp/cptools/zephyr2cp.py +++ b/ports/zephyr-cp/cptools/zephyr2cp.py @@ -1,5 +1,6 @@ import logging import pathlib +import re import cpbuild import yaml @@ -12,7 +13,14 @@ # GPIO flags defined here: include/zephyr/dt-bindings/gpio/gpio.h GPIO_ACTIVE_LOW = 1 << 0 -MINIMUM_RAM_SIZE = 1024 +# A region has to be big enough to host TLSF's control structure to be usable as +# the first heap pool, and TLSF sizes that structure from the maximum heap size +# rather than from the region: at an 8 MB maximum it is 2412 bytes. Anything +# smaller than this is not worth adding as a later pool either. The previous +# value of 1024 let through two SiWx917 regions that are exactly 0x400 bytes, +# /memory@0 (reserved for the network processor) and /memory-dma@24061c00, +# neither of which should ever be in the Python heap. +MINIMUM_RAM_SIZE = 8192 MANUAL_COMPAT_TO_DRIVER = { "renesas_ra_nv_flash": "flash", @@ -99,6 +107,29 @@ "D12", "D13", ], + "adafruit-clue": [ + ["P0", "D0", "A2", "RX"], + ["P1", "D1", "A3", "TX"], + ["P2", "D2", "A4"], + ["P3", "D3", "A5"], + ["P4", "D4", "A6"], + ["P5", "D5", "BUTTON_A"], + ["P6", "D6"], + ["P7", "D7"], + ["P8", "D8"], + ["P9", "D9"], + ["P10", "D10", "A7"], + ["P11", "D11", "BUTTON_B"], + ["P12", "D12", "A0"], + ["P13", "D13", "SCK"], + ["P14", "D14", "MISO"], + ["P15", "D15", "MOSI"], + ["P16", "D16", "A1"], + ["P17", "D17", "L", "LED"], + ["P18", "D18", "NEOPIXEL"], + ["P19", "D19", "SCL"], + ["P20", "D20", "SDA"], + ], "nordic,expansion-board-header": [ "P1_04", "P1_05", @@ -451,6 +482,33 @@ def find_ram_regions(device_tree): return rams +# gpio-keys nodes identify a key with `zephyr,code` rather than the optional and +# deprecated `label`, so the code is the only name a modern board gives. +INPUT_KEY_NAMES = {} + + +def _populate_input_key_names(): + header = ( + pathlib.Path(__file__).parent.parent + / "zephyr" + / "include" + / "zephyr" + / "dt-bindings" + / "input" + / "input-event-codes.h" + ) + if not header.exists(): + return + pattern = re.compile(r"^#define\s+INPUT_(?PKEY_\w+)\s+(?P\d+)") + for line in header.read_text().splitlines(): + match = pattern.match(line) + if match: + INPUT_KEY_NAMES.setdefault(int(match.group("code")), match.group("name")) + + +_populate_input_key_names() + + @cpbuild.run_in_thread def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir, mpconfigboard=None): # noqa: C901 board_dir = builddir / "board" @@ -494,7 +552,15 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir, mpconfig else: board_yaml = board_yaml["board"] board_info["vendor_id"] = board_yaml["vendor"] - vendor_index = zephyr_board_dir.parent / "index.rst" + # Most vendors put boards directly in boards//, but some group + # them further, like boards/silabs/dev_kits/. Walk up to the directory + # named for the vendor so we read the vendor's index.rst and not a category's. + vendor_dir = zephyr_board_dir.parent + for parent in zephyr_board_dir.parents: + if parent.name == board_info["vendor_id"]: + vendor_dir = parent + break + vendor_index = vendor_dir / "index.rst" if vendor_index.exists(): vendor_index = vendor_index.read_text() vendor_index = vendor_index.split("\n") @@ -526,6 +592,7 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir, mpconfig board_names = {} status_led = None status_led_inverted = False + boot_button = None path2chosen = {} chosen2path = {} @@ -671,17 +738,34 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir, mpconfig if "gpio-keys" in compatible: for key in node.nodes: - props = node.nodes[key].props + key_node = node.nodes[key] + props = key_node.props ioport = props["gpios"]._markers[1][2] num = int.from_bytes(props["gpios"].value[4:8], "big") if (ioport, num) not in board_names: board_names[(ioport, num)] = [] - board_names[(ioport, num)].append(props["label"].to_string()) - if key in node2alias: - if "sw0" in node2alias[key]: + # `label` is optional and deprecated on gpio-keys. Modern + # boards identify keys with `zephyr,code`, so fall back to the + # name of that code. + if "label" in props: + board_names[(ioport, num)].append(props["label"].to_string()) + elif "zephyr,code" in props: + key_code = props["zephyr,code"].to_num() + if key_code in INPUT_KEY_NAMES: + board_names[(ioport, num)].append(INPUT_KEY_NAMES[key_code]) + if key_node in node2alias: + aliases = node2alias[key_node] + if "sw0" in aliases: board_names[(ioport, num)].append("BUTTON") - board_names[(ioport, num)].extend(node2alias[key]) + # The sw0 alias designates the conventional first user + # button, so prefer it as the boot button. + boot_button = (ioport, num) + board_names[(ioport, num)].extend(aliases) + # Default to the first button in device tree order when no sw0 + # alias has designated one yet. + if boot_button is None: + boot_button = (ioport, num) if len(all_ioports) > 1: a, b = all_ioports[:2] @@ -708,6 +792,8 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir, mpconfig pin_object_name = f"P{ioport[len(shared_prefix) :].upper()}_{num:02d}" if status_led and (ioport, num) == status_led: status_led = pin_object_name + if boot_button and (ioport, num) == boot_button: + boot_button = pin_object_name pin_defs.append( f"const mcu_pin_obj_t pin_{pin_object_name} = {{ .base.type = &mcu_pin_type, .port = DEVICE_DT_GET(DT_NODELABEL({ioport})), .number = {num}}};" ) @@ -870,6 +956,10 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir, mpconfig else: status_led = "" status_led_inverted = "" + if boot_button: + boot_button = f"#define CIRCUITPY_BOOT_BUTTON (&pin_{boot_button})\n" + else: + boot_button = "" ram_list = [] ram_externs = [] max_size = 0 @@ -899,6 +989,7 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir, mpconfig #define CIRCUITPY_RAM_DEVICE_COUNT {len(rams)} {status_led} {status_led_inverted} +{boot_button} """ if not header.exists() or header.read_text() != new_header_content: header.write_text(new_header_content) diff --git a/ports/zephyr-cp/debug.conf b/ports/zephyr-cp/debug.conf index 90c1f52d4db..2f12db119f8 100644 --- a/ports/zephyr-cp/debug.conf +++ b/ports/zephyr-cp/debug.conf @@ -4,7 +4,6 @@ CONFIG_LOG_MAX_LEVEL=4 CONFIG_STACK_SENTINEL=y CONFIG_DEBUG_THREAD_INFO=y -CONFIG_DEBUG_INFO=y CONFIG_EXCEPTION_STACK_TRACE=y CONFIG_ASSERT=y @@ -13,3 +12,41 @@ CONFIG_FRAME_POINTER=y CONFIG_FLASH_LOG_LEVEL_DBG=y CONFIG_LOG_MODE_IMMEDIATE=y + +# Quiet the UDC device-controller drivers in the debug build. The DWC2 driver +# logs "Prepare RX 0x%02x doeptsiz 0x%x" (LOG_INF) on every OUT endpoint RX +# prepare, which is noisy. Drop UDC_DRIVER to WRN to keep errors/warnings +# while compiling out INF/DBG. (Shared by all udc drivers; only one is active +# per board.) +CONFIG_UDC_DRIVER_LOG_LEVEL_WRN=y + +# Bluetooth: enable BT host debug logging so success paths print, not just +# LOG_ERR. In particular bt_keys logs "Stored keys for " (LOG_DBG) on a +# successful bt_keys_store(); without this only the failure message +# "Failed to save keys (err -116)" is visible. Also enable the adjacent +# modules (SMP drives pairing, BT_SETTINGS is the NVS backend that returns +# the store err) so the -EALREADY (-116) store-failure context is complete. +CONFIG_BT_KEYS_LOG_LEVEL_DBG=y +CONFIG_BT_SETTINGS_LOG_LEVEL_DBG=y +CONFIG_BT_SMP_LOG_LEVEL_DBG=y + +# Bump stacks for debug build - verbose logging consumes more stack. +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 +CONFIG_MAIN_STACK_SIZE=32768 +CONFIG_ISR_STACK_SIZE=4096 +CONFIG_UDC_DWC2_STACK_SIZE=4096 +CONFIG_UDC_NRF_THREAD_STACK_SIZE=4096 +CONFIG_USBD_THREAD_STACK_SIZE=4096 +CONFIG_USBD_MSC_STACK_SIZE=4096 +CONFIG_IDLE_STACK_SIZE=1024 + +# Bluetooth stacks: debug logging + assertions + LOG_MODE_IMMEDIATE consume +# substantial extra stack in the BT host RX workqueue (runs all event callbacks, +# including SMP/pairing) and the long workqueue (deferred SMP/pairing/crypto + +# NVS bond-key store). Zephyr's own SMP/pairing test app +# (tests/bluetooth/tester/prj.conf) uses 4096 for BT_RX_STACK_SIZE. +# (BT_CTLR_RX_PRIO_STACK_SIZE is bumped in the Zephyr controller Kconfig +# default since it's a hidden symbol. BT_HCI_TX/BT_CTLR_RX are also hidden and +# haven't overflowed — left at their defaults.) +CONFIG_BT_RX_STACK_SIZE=4096 +CONFIG_BT_LONG_WQ_STACK_SIZE=4096 diff --git a/ports/zephyr-cp/prj.conf b/ports/zephyr-cp/prj.conf index 765132742bb..864af4c6112 100644 --- a/ports/zephyr-cp/prj.conf +++ b/ports/zephyr-cp/prj.conf @@ -1,6 +1,7 @@ CONFIG_SYS_HEAP_RUNTIME_STATS=n CONFIG_FLASH=y CONFIG_FLASH_MAP=y +CONFIG_USE_DT_CODE_PARTITION=y CONFIG_DYNAMIC_INTERRUPTS=y CONFIG_UART_INTERRUPT_DRIVEN=y @@ -8,6 +9,11 @@ CONFIG_UART_INTERRUPT_DRIVEN=y CONFIG_FLASH_MAP_LABELS=y CONFIG_MAIN_STACK_SIZE=24288 +# The system workqueue is used by BT settings delayed store (NVS flash writes +# during bond key save). RRAM flash drivers on nRF54L need more than the +# default 1024 bytes to avoid stack overflow. +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=3072 + CONFIG_THREAD_STACK_INFO=y CONFIG_ARCH_POSIX_UPDATE_STACK_INFO=y CONFIG_STACK_SENTINEL=n @@ -16,6 +22,7 @@ CONFIG_EXCEPTION_STACK_TRACE=n CONFIG_USB_DEVICE_STACK_NEXT=y CONFIG_USBD_CDC_ACM_CLASS=y +CONFIG_USBD_HID_SUPPORT=y CONFIG_USBD_MSC_STACK_SIZE=1536 CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT=n @@ -24,19 +31,12 @@ CONFIG_USBD_MSC_LUNS_PER_INSTANCE=1 CONFIG_HWINFO=y CONFIG_REBOOT=y -CONFIG_ENTROPY_GENERATOR=y - CONFIG_ASSERT=n -CONFIG_LOG_BLOCK_IN_THREAD=n +CONFIG_LOG_BLOCK_IN_THREAD=y CONFIG_EVENTS=y CONFIG_SERIAL=y -CONFIG_UART_LINE_CTRL=y - -CONFIG_I2C=y -CONFIG_SPI=y -CONFIG_SPI_ASYNC=y CONFIG_LOG=y CONFIG_LOG_MAX_LEVEL=2 @@ -46,13 +46,33 @@ CONFIG_NET_HOSTNAME_ENABLE=y CONFIG_NET_HOSTNAME_DYNAMIC=y CONFIG_NET_HOSTNAME="circuitpython" -CONFIG_I2S=y CONFIG_DYNAMIC_THREAD=y CONFIG_DYNAMIC_THREAD_ALLOC=y CONFIG_DYNAMIC_THREAD_PREFER_ALLOC=y +CONFIG_FPU=y + CONFIG_MBEDTLS=y + +# Override Kconfig default not taking effect. The BLE file-transfer workflow +# packs up to BLEIO_PACKET_BUFFER_MAX_PACKET_SIZE (512) bytes per notification, +# so the L2CAP/ATT MTU must accommodate a 512-byte payload plus the 3-byte +# ATT header (opcode + handle). Without this the default with SMP is only 65, +# and notifications larger than the negotiated MTU fail with +# "No ATT channel for MTU". +CONFIG_BT_L2CAP_TX_MTU=515 +CONFIG_BT_BUF_ACL_RX_SIZE=255 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_RX_STACK_SIZE=2048 +CONFIG_BT_LONG_WQ_STACK_SIZE=3072 CONFIG_MBEDTLS_BUILTIN=y -CONFIG_MBEDTLS_PSA_CRYPTO_C=y +CONFIG_PSA_CRYPTO=y CONFIG_PSA_WANT_ALG_SHA_1=y CONFIG_PSA_WANT_ALG_SHA_256=y + +# Settings storage backend for BT bond keys +# SECTOR_COUNT is set high — the init code caps it to what fits in the storage +# partition: min(sector_count, partition_size / sector_size) +CONFIG_NVS=y +CONFIG_SETTINGS_NVS=y +CONFIG_SETTINGS_NVS_SECTOR_COUNT=256 diff --git a/ports/zephyr-cp/supervisor/port.c b/ports/zephyr-cp/supervisor/port.c index 1ecde0b0141..cdbb885be0a 100644 --- a/ports/zephyr-cp/supervisor/port.c +++ b/ports/zephyr-cp/supervisor/port.c @@ -22,8 +22,10 @@ #if defined(CONFIG_ARCH_POSIX) #include +#include #include "cmdline.h" +#include "nsi_host_trampolines.h" #include "posix_board_if.h" #include "posix_native_task.h" #endif @@ -65,7 +67,13 @@ static struct k_timer tick_timer; static int32_t native_sim_vm_runs = INT32_MAX; static uint32_t native_sim_reset_port_count = 0; -static struct args_struct_t native_sim_reset_port_args[] = { +// Path to a file used to preserve retained memory across the execv reboot, or +// NULL if disabled. Set with --retained-memory= (see +// cp_saved_word_save/restore()). Currently persists the safe-mode saved word; +// intended to also preserve sleep RAM in the future. +static const char *native_sim_retained_memory; + +static struct args_struct_t native_sim_port_args[] = { { .option = "vm-runs", .name = "count", @@ -74,11 +82,20 @@ static struct args_struct_t native_sim_reset_port_args[] = { .descript = "Exit native_sim after this many VM runs. " "Example: --vm-runs=2" }, + { + .option = "retained-memory", + .name = "path", + .type = 's', + .dest = (void *)&native_sim_retained_memory, + .descript = "File used to preserve retained memory (e.g. the safe-mode " + "saved word) across the process re-exec reboot. " + "Example: --retained-memory=/tmp/cp_retained.bin" + }, ARG_TABLE_ENDMARKER }; static void native_sim_register_cmdline_opts(void) { - native_add_command_line_opts(native_sim_reset_port_args); + native_add_command_line_opts(native_sim_port_args); } NATIVE_TASK(native_sim_register_cmdline_opts, PRE_BOOT_1, 0); @@ -135,7 +152,56 @@ static void _tick_function(struct k_timer *timer_id) { supervisor_tick(); } +// Save and retrieve a word from memory that is preserved over reset. Used for safe mode. +static __noinit uint32_t cp_saved_word; + +void port_set_saved_word(uint32_t value) { + cp_saved_word = value; +} + +uint32_t port_get_saved_word(void) { + return cp_saved_word; +} + +// Save and restore retained memory across the native_sim/bsim reboot. +// Opt in with --retained-memory=. +#if defined(CONFIG_ARCH_POSIX) +static void cp_saved_word_save(void) { + const char *path = native_sim_retained_memory; + if (path == NULL || path[0] == '\0') { + return; + } + int fd = nsi_host_open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + return; + } + uint32_t value = cp_saved_word; + (void)nsi_host_write(fd, &value, sizeof(value)); + (void)nsi_host_close(fd); +} + +static void cp_saved_word_restore(void) { + const char *path = native_sim_retained_memory; + if (path == NULL || path[0] == '\0') { + return; + } + int fd = nsi_host_open(path, O_RDONLY, 0 /* unused */); + if (fd < 0) { + return; // First boot: no save file yet. + } + uint32_t value = 0; + (void)nsi_host_read(fd, &value, sizeof(value)); + (void)nsi_host_close(fd); + cp_saved_word = value; +} +#endif + safe_mode_t port_init(void) { + #if defined(CONFIG_ARCH_POSIX) + // Restore the saved word (if any) before wait_for_safe_mode_reset reads it. + cp_saved_word_restore(); + #endif + // We run CircuitPython at the lowest priority (just higher than idle.) // This allows networking and USB to preempt us. k_thread_priority_set(k_current_get(), CONFIG_NUM_PREEMPT_PRIORITIES - 1); @@ -146,6 +212,11 @@ safe_mode_t port_init(void) { // Reset the microcontroller completely. void reset_cpu(void) { + #if defined(CONFIG_ARCH_POSIX) + // Persist the saved word across the process re-exec reboot. + cp_saved_word_save(); + #endif + // Try a warm reboot first. It won't return if it works but isn't always // implemented. sys_reboot(SYS_REBOOT_WARM); @@ -206,14 +277,6 @@ uint32_t *port_stack_get_top(void) { return (uint32_t *)(stack_info.start + stack_info.size - stack_info.delta); } -// Save and retrieve a word from memory that is preserved over reset. Used for safe mode. -void port_set_saved_word(uint32_t) { - -} -uint32_t port_get_saved_word(void) { - return 0; -} - uint64_t port_get_raw_ticks(uint8_t *subticks) { // Make sure time advances in the simulator. #if defined(CONFIG_ARCH_POSIX) @@ -294,6 +357,14 @@ void port_heap_init(void) { // If this crashes, then make sure you've enabled all of the Kconfig needed for the drivers. if (valid_pool_count == 0) { heap = tlsf_create_with_pool(heap_bottom, size, circuitpy_max_ram_size); + if (heap == NULL) { + // Can happen for a region the linker filled almost to the top, + // which the build-time MINIMUM_RAM_SIZE filter cannot predict + // because it only sees the devicetree size. + printk("Heap creation failed at %p; trying the next region\n", heap_bottom); + pools[i] = NULL; + continue; + } pools[i] = tlsf_get_pool(heap); } else { pools[i] = tlsf_add_pool(heap, heap_bottom + 1, size - sizeof(uint32_t)); diff --git a/ports/zephyr-cp/supervisor/usb.c b/ports/zephyr-cp/supervisor/usb.c index a42a5192f4f..6d3bf3286dd 100644 --- a/ports/zephyr-cp/supervisor/usb.c +++ b/ports/zephyr-cp/supervisor/usb.c @@ -3,6 +3,10 @@ #include "shared-bindings/usb_cdc/__init__.h" #include "shared-bindings/usb_cdc/Serial.h" +#if CIRCUITPY_USB_HID +#include "common-hal/usb_hid/__init__.h" +#endif + #include "supervisor/zephyr-cp.h" #include "extmod/vfs.h" @@ -16,6 +20,9 @@ #include #include #include +#if DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_usbd) +#include +#endif #include "shared-module/storage/__init__.h" #include "supervisor/filesystem.h" @@ -189,22 +196,38 @@ int _zephyr_disk_ioctl(struct disk_info *disk, uint8_t cmd, void *buff) { return 0; } +// Tracks whether the USB host has selected a configuration, i.e. the device has +// enumerated. Written from the USBD message callback (USBD thread or system +// workqueue context) and read from the CircuitPython VM thread, so keep it +// volatile to avoid the compiler caching stale values. +static volatile bool usb_configured = false; + static void _msg_cb(struct usbd_context *const ctx, const struct usbd_msg *msg) { LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type)); if (usbd_can_detect_vbus(ctx)) { if (msg->type == USBD_MSG_VBUS_READY) { + usb_configured = false; if (usbd_enable(ctx)) { LOG_ERR("Failed to enable device support"); } } if (msg->type == USBD_MSG_VBUS_REMOVED) { + usb_configured = false; if (usbd_disable(ctx)) { LOG_ERR("Failed to disable device support"); } } } + + // A non-zero configuration value means the host has enumerated us. + // A value of zero (or a bus reset / VBUS removal) unconfigures us. + if (msg->type == USBD_MSG_CONFIGURATION) { + usb_configured = (msg->status != 0); + } else if (msg->type == USBD_MSG_RESET) { + usb_configured = false; + } } void usb_init(void) { @@ -293,6 +316,17 @@ void usb_init(void) { printk("Registered MSC class for high speed\n"); } + #if CIRCUITPY_USB_HID + if (usb_hid_enabled()) { + printk("Registering High-Speed hid class\n"); + err = usbd_register_class(&main_usbd, "hid_0", USBD_SPEED_HS, 1); + if (err) { + printk("Failed to register HID class (HS) %d\n", err); + return; + } + } + #endif + usbd_device_set_code_triple(&main_usbd, USBD_SPEED_HS, USB_BCC_MISCELLANEOUS, 0x02, 0x01); } @@ -334,6 +368,17 @@ void usb_init(void) { } /* doc functions register end */ + #if CIRCUITPY_USB_HID + if (usb_hid_enabled()) { + printk("Registering Full-Speed hid class\n"); + err = usbd_register_class(&main_usbd, "hid_0", USBD_SPEED_FS, 1); + if (err) { + printk("Failed to register HID class (FS) %d\n", err); + return; + } + } + #endif + usbd_device_set_code_triple(&main_usbd, USBD_SPEED_FS, USB_BCC_MISCELLANEOUS, 0x02, 0x01); printk("Setting self powered\n"); @@ -346,27 +391,63 @@ void usb_init(void) { return; } + #if CIRCUITPY_USB_HID + printk("Initializing HID\n"); + usb_hid_init_devices(); + #endif + printk("usbd_init\n"); err = usbd_init(&main_usbd); if (err) { - LOG_ERR("Failed to initialize device support"); + LOG_ERR("Failed to initialize device support (%d)", err); return; } printk("USB initialized\n"); if (!usbd_can_detect_vbus(&main_usbd)) { + /* No VBUS detection — just enable unconditionally. */ err = usbd_enable(&main_usbd); if (err) { - LOG_ERR("Failed to enable device support"); + LOG_ERR("Failed to enable device support (%d)", err); return; } - printk("usbd enabled\n"); + } else { + #if DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_usbd) + /* + * On nRF52 USBD, the UDC driver enables VBUS detection interrupts + * inside usbd_init(). If USB is already plugged in, the VBUS_READY + * event arrives before usbd_init() sets status.initialized = true, + * causing a __ASSERT in usbd_thread (with CONFIG_ASSERT=y) that kills + * the event-processing thread. Our _msg_cb then never receives + * VBUS_READY, and USB never enumerates. + * + * Work around the race by checking the current VBUS state directly. + * udc_nrf_enable() does not require VBUS to be present, so it's safe + * to call usbd_enable() if VBUS is already up. + */ + nrfx_power_usb_state_t vbus = nrfx_power_usbstatus_get(); + if (vbus != NRFX_POWER_USB_STATE_DISCONNECTED) { + err = usbd_enable(&main_usbd); + if (err) { + LOG_ERR("Failed to enable device support (%d)", err); + return; + } + } + #else + /* + * On nRF54 USBHS, VBUS events arrive via NRFS IPC which has a natural + * startup delay, so the assert race doesn't occur. Additionally, + * udc_enable() blocks until VBUS is ready, so we must NOT call + * usbd_enable() here — let _msg_cb handle it on VBUS_READY. + */ + #endif } } bool usb_connected(void) { - return false; + // Mirrors TinyUSB's tud_ready(): configured (mounted) and not suspended. + return usb_configured && !usbd_is_suspended(&main_usbd); } void usb_disconnect(void) { diff --git a/ports/zephyr-cp/sysbuild.cmake b/ports/zephyr-cp/sysbuild.cmake index 3c3acf0a803..bea5c588a2a 100644 --- a/ports/zephyr-cp/sysbuild.cmake +++ b/ports/zephyr-cp/sysbuild.cmake @@ -13,8 +13,8 @@ if(SB_CONFIG_NET_CORE_IMAGE_HCI_IPC) BOARD ${SB_CONFIG_NET_CORE_BOARD} ) - set(${NET_APP}_CONF_FILE - ${NET_APP_SRC_DIR}/nrf5340_cpunet_iso-bt_ll_sw_split.conf + set(${NET_APP}_OVERLAY_CONFIG + ${NET_APP_SRC_DIR}/extra-cis-bt_ll_sw_split.conf CACHE INTERNAL "" ) diff --git a/ports/zephyr-cp/sysbuild/hci_ipc.conf b/ports/zephyr-cp/sysbuild/hci_ipc.conf new file mode 100644 index 00000000000..20dc067f3e4 --- /dev/null +++ b/ports/zephyr-cp/sysbuild/hci_ipc.conf @@ -0,0 +1,3 @@ +# Enable dynamic TX power control so that BT_HCI_OP_VS_WRITE_TX_POWER_LEVEL +# is supported by the controller. +CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL=y diff --git a/ports/zephyr-cp/tests/__init__.py b/ports/zephyr-cp/tests/__init__.py index e9039dd88b3..c042d744d7d 100644 --- a/ports/zephyr-cp/tests/__init__.py +++ b/ports/zephyr-cp/tests/__init__.py @@ -138,6 +138,43 @@ def __init__(self, cmd, timeout=5, trace_file=None, env=None, flash_file=None): self.debug_serial = SerialSaver( StdSerial(self._proc.stdin, self._proc.stdout), name="debug" ) + # Offset into debug_serial output for finding the next UART PTY path. + self._pty_search_offset = 0 + + def reconnect_serial(self, timeout=30.0): + """Wait for the simulator to reboot (execv) and reopen the UART. + + native_sim/bsim reboot by re-executing the process; the UART PTY master + fd is O_CLOEXEC so it closes on execv and a new PTY is opened. The new + "connected to pseudotty: " line is printed to the process stdout + (which survives execv), so it shows up in debug_serial. This method waits + for that line and reopens ``self.serial`` on the new PTY. + """ + import time + + marker = "connected to pseudotty:" + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + if self._proc.poll() is not None: + return False + out = self.debug_serial.all_output + idx = out.find(marker, self._pty_search_offset) + newline = out.find("\n", idx) if idx >= 0 else -1 + if idx >= 0 and newline >= 0: + line = out[idx:newline] + pty_path = line.strip().rsplit(":", maxsplit=1)[1].strip() + self._pty_search_offset = newline + 1 + try: + self.serial.close() + except Exception: + pass + self.serial = SerialSaver( + serial.Serial(pty_path, baudrate=115200, timeout=0.05, write_timeout=0), + name="uart0", + ) + return True + time.sleep(0.05) + return False def shutdown(self): if self._proc.poll() is None: diff --git a/ports/zephyr-cp/tests/bsim/__init__.py b/ports/zephyr-cp/tests/bsim/__init__.py index 75136bccf43..e69de29bb2d 100644 --- a/ports/zephyr-cp/tests/bsim/__init__.py +++ b/ports/zephyr-cp/tests/bsim/__init__.py @@ -1,3 +0,0 @@ -import pytest - -pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") diff --git a/ports/zephyr-cp/tests/bsim/conftest.py b/ports/zephyr-cp/tests/bsim/conftest.py index 493f4c92b3b..a0c2c9fca27 100644 --- a/ports/zephyr-cp/tests/bsim/conftest.py +++ b/ports/zephyr-cp/tests/bsim/conftest.py @@ -16,9 +16,6 @@ logger = logging.getLogger(__name__) ZEPHYR_CP = Path(__file__).resolve().parents[2] -BSIM_BUILD_DIR = ZEPHYR_CP / "build-native_nrf5340bsim" -BSIM_SYSBUILD_BINARY = BSIM_BUILD_DIR / "zephyr/zephyr.exe" -BSIM_BINARY = BSIM_BUILD_DIR / "zephyr-cp/zephyr/zephyr.exe" BSIM_ROOT = ZEPHYR_CP / "tools/bsim" BSIM_PHY_BINARY = BSIM_ROOT / "bin/bs_2G4_phy_v1" @@ -34,14 +31,14 @@ def native_sim_env() -> dict[str, str]: return env -@pytest.fixture -def bsim_binary(): - """Return path to nrf5340bsim binary, skip if not built.""" - if BSIM_SYSBUILD_BINARY.exists(): - return BSIM_SYSBUILD_BINARY - if not BSIM_BINARY.exists(): - pytest.skip(f"nrf5340bsim not built: {BSIM_BINARY}") - return BSIM_BINARY +@pytest.fixture(params=["native_nrf5340bsim", "native_nrf54lm20bsim"]) +def board(request): + """Parametrized board fixture for bsim tests. + + Overrides the parent conftest's board fixture to run each test on both + bsim boards. + """ + return request.param @pytest.fixture @@ -177,6 +174,8 @@ def zephyr_sample(request, bsim_phy, native_sim_env, sim_id): sample_rel = str(sample).removeprefix("zephyr/samples/") source_dir = ZEPHYR_CP / "zephyr/samples" / sample_rel + if not source_dir.exists(): + source_dir = ZEPHYR_CP / sample # port-local sample if not source_dir.exists(): pytest.skip(f"Zephyr sample not found: {source_dir}") @@ -184,7 +183,21 @@ def zephyr_sample(request, bsim_phy, native_sim_env, sim_id): build_dir = ZEPHYR_CP / build_name binary = build_dir / "zephyr/zephyr.exe" + needs_rebuild = False if not binary.exists(): + needs_rebuild = True + else: + # Rebuild if any source file is newer than the binary. + binary_mtime = binary.stat().st_mtime + for root, _dirs, files in os.walk(source_dir): + for f in files: + if Path(root, f).stat().st_mtime > binary_mtime: + needs_rebuild = True + break + if needs_rebuild: + break + + if needs_rebuild: try: binary = _build_zephyr_sample(build_dir, source_dir, board) except (subprocess.CalledProcessError, RuntimeError) as exc: @@ -215,6 +228,39 @@ def zephyr_sample(request, bsim_phy, native_sim_env, sim_id): print(sample_proc.serial.all_output) +FROZEN_ROOT = (ZEPHYR_CP / "../../frozen").resolve() + + +def get_library_files(name: str) -> dict[str, str]: + """Return a dict mapping CIRCUITPY paths to file contents for all + .py files in a frozen library, suitable for circuitpy_drive. + + Looks up ``name`` under the ``frozen/`` directory (e.g. + ``get_library_files("adafruit_ble")`` walks + ``frozen/Adafruit_CircuitPython_BLE/adafruit_ble/``). + """ + files: dict[str, str] = {} + + # Try directory-style library: *//**/*.py + py_files = sorted(FROZEN_ROOT.glob(f"*/{name}/**/*.py")) + if py_files: + repo_dir = py_files[0].relative_to(FROZEN_ROOT).parts[0] + lib_parent = FROZEN_ROOT / repo_dir + for py_file in py_files: + rel = py_file.relative_to(lib_parent) + files[str(rel)] = py_file.read_text(encoding="utf-8") + return files + + # Try single-file module: */.py + py_files = list(FROZEN_ROOT.glob(f"*/{name}.py")) + if py_files: + py_file = py_files[0] + files[name + ".py"] = py_file.read_text(encoding="utf-8") + return files + + raise FileNotFoundError(f"Library {name!r} not found under {FROZEN_ROOT}") + + # pytest markers are defined inside out meaning the bottom one is first in the # list and the top is last. So use negative indices to reverse them. @pytest.fixture diff --git a/ports/zephyr-cp/tests/bsim/samples/central_battery_client/CMakeLists.txt b/ports/zephyr-cp/tests/bsim/samples/central_battery_client/CMakeLists.txt new file mode 100644 index 00000000000..d3635c95059 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/samples/central_battery_client/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(central_battery_client) + +target_sources(app PRIVATE + src/main.c +) diff --git a/ports/zephyr-cp/tests/bsim/samples/central_battery_client/prj.conf b/ports/zephyr-cp/tests/bsim/samples/central_battery_client/prj.conf new file mode 100644 index 00000000000..56a790677be --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/samples/central_battery_client/prj.conf @@ -0,0 +1,4 @@ +CONFIG_BT=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_GATT_CLIENT=y +CONFIG_LOG=y diff --git a/ports/zephyr-cp/tests/bsim/samples/central_battery_client/src/main.c b/ports/zephyr-cp/tests/bsim/samples/central_battery_client/src/main.c new file mode 100644 index 00000000000..34ed2fa09b9 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/samples/central_battery_client/src/main.c @@ -0,0 +1,212 @@ +/* + * SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries + * SPDX-License-Identifier: MIT + * + * Zephyr BLE central that connects to a device named "CPSVC", + * discovers Battery Service (0x180F), reads Battery Level (0x2A19), + * prints the value, and disconnects. + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include + +static struct bt_conn *default_conn; +static struct bt_gatt_discover_params discover_params; +static struct bt_gatt_read_params read_params; + +/* Battery Service UUID: 0x180F */ +static struct bt_uuid_16 bas_uuid = BT_UUID_INIT_16(0x180F); +/* Battery Level Characteristic UUID: 0x2A19 */ +static struct bt_uuid_16 bat_level_uuid = BT_UUID_INIT_16(0x2A19); + +static uint16_t bat_level_handle; + +static void start_scan(void); + +static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, + struct net_buf_simple *ad) { + if (default_conn) { + return; + } + + /* Only interested in connectable devices */ + if (type != BT_GAP_ADV_TYPE_ADV_IND && + type != BT_GAP_ADV_TYPE_ADV_DIRECT_IND) { + return; + } + + /* Clone ad data so we can parse it without affecting the original */ + struct net_buf_simple ad_copy; + uint8_t ad_buf[64]; + size_t copy_len = ad->len < sizeof(ad_buf) ? ad->len : sizeof(ad_buf); + memcpy(ad_buf, ad->data, copy_len); + net_buf_simple_init_with_data(&ad_copy, ad_buf, copy_len); + + char addr_str[BT_ADDR_LE_STR_LEN]; + bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); + printk("Found CPSVC device: %s (RSSI %d)\n", addr_str, rssi); + + if (bt_le_scan_stop()) { + return; + } + + int err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, + BT_LE_CONN_PARAM_DEFAULT, &default_conn); + if (err) { + printk("Create conn failed (%d)\n", err); + start_scan(); + } +} + +static void start_scan(void) { + int err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found); + if (err) { + printk("Scanning failed to start (err %d)\n", err); + return; + } + printk("Scanning started\n"); +} + +static uint8_t read_battery_level_cb(struct bt_conn *conn, uint8_t err, + struct bt_gatt_read_params *params, + const void *data, uint16_t length) { + if (err) { + printk("Read failed (err %d)\n", err); + } else if (data && length >= 1) { + uint8_t level = ((const uint8_t *)data)[0]; + printk("Battery Level: %d\n", level); + } else { + printk("Read returned no data\n"); + } + + /* Disconnect after reading */ + bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + + return BT_GATT_ITER_STOP; +} + +static uint8_t discover_char_cb(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params) { + if (!attr) { + printk("Characteristic discovery complete\n"); + return BT_GATT_ITER_STOP; + } + + struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data; + + if (bt_uuid_cmp(chrc->uuid, &bat_level_uuid.uuid) == 0) { + bat_level_handle = chrc->value_handle; + printk("Found Battery Level characteristic, handle: %u\n", bat_level_handle); + + /* Read the battery level */ + read_params.func = read_battery_level_cb; + read_params.handle_count = 1; + read_params.single.handle = bat_level_handle; + read_params.single.offset = 0; + + int err = bt_gatt_read(conn, &read_params); + if (err) { + printk("Read request failed (err %d)\n", err); + } + return BT_GATT_ITER_STOP; + } + + return BT_GATT_ITER_CONTINUE; +} + +static uint8_t discover_service_cb(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params) { + if (!attr) { + printk("Service discovery complete, BAS not found\n"); + bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + return BT_GATT_ITER_STOP; + } + + printk("Found Battery Service, handle: %u\n", attr->handle); + + /* Now discover characteristics within this service */ + discover_params.uuid = &bat_level_uuid.uuid; + discover_params.start_handle = attr->handle + 1; + discover_params.end_handle = 0xFFFF; + discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; + discover_params.func = discover_char_cb; + + int err = bt_gatt_discover(conn, &discover_params); + if (err) { + printk("Characteristic discovery failed (err %d)\n", err); + } + + return BT_GATT_ITER_STOP; +} + +static void connected(struct bt_conn *conn, uint8_t err) { + char addr[BT_ADDR_LE_STR_LEN]; + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + if (err) { + printk("Failed to connect to %s (%u)\n", addr, err); + bt_conn_unref(default_conn); + default_conn = NULL; + start_scan(); + return; + } + + if (conn != default_conn) { + return; + } + + printk("Connected: %s\n", addr); + + /* Discover Battery Service */ + discover_params.uuid = &bas_uuid.uuid; + discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE; + discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE; + discover_params.type = BT_GATT_DISCOVER_PRIMARY; + discover_params.func = discover_service_cb; + + err = bt_gatt_discover(conn, &discover_params); + if (err) { + printk("Service discovery failed (err %d)\n", err); + } +} + +static void disconnected(struct bt_conn *conn, uint8_t reason) { + char addr[BT_ADDR_LE_STR_LEN]; + + if (conn != default_conn) { + return; + } + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + printk("Disconnected: %s (reason 0x%02x)\n", addr, reason); + + bt_conn_unref(default_conn); + default_conn = NULL; +} + +BT_CONN_CB_DEFINE(conn_callbacks) = { + .connected = connected, + .disconnected = disconnected, +}; + +int main(void) { + int err = bt_enable(NULL); + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return 0; + } + + printk("Bluetooth initialized\n"); + start_scan(); + return 0; +} diff --git a/ports/zephyr-cp/tests/bsim/samples/central_nus_client/CMakeLists.txt b/ports/zephyr-cp/tests/bsim/samples/central_nus_client/CMakeLists.txt new file mode 100644 index 00000000000..d5deacef980 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/samples/central_nus_client/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(central_nus_client) + +target_sources(app PRIVATE + src/main.c +) diff --git a/ports/zephyr-cp/tests/bsim/samples/central_nus_client/prj.conf b/ports/zephyr-cp/tests/bsim/samples/central_nus_client/prj.conf new file mode 100644 index 00000000000..56a790677be --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/samples/central_nus_client/prj.conf @@ -0,0 +1,4 @@ +CONFIG_BT=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_GATT_CLIENT=y +CONFIG_LOG=y diff --git a/ports/zephyr-cp/tests/bsim/samples/central_nus_client/src/main.c b/ports/zephyr-cp/tests/bsim/samples/central_nus_client/src/main.c new file mode 100644 index 00000000000..9825a3e7a5e --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/samples/central_nus_client/src/main.c @@ -0,0 +1,302 @@ +/* + * SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries + * SPDX-License-Identifier: MIT + * + * Zephyr BLE central that connects to a device named "CPNUS", + * discovers Nordic UART Service (NUS), writes to the RX characteristic, + * subscribes to TX notifications, receives data, and disconnects. + * + * NUS UUIDs: + * Service: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E + * TX (peripheral→central, notify): 6E400003-B5A3-F393-E0A9-E50E24DCCA9E + * RX (central→peripheral, write): 6E400002-B5A3-F393-E0A9-E50E24DCCA9E + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include + +static struct bt_conn *default_conn; +static struct bt_gatt_discover_params discover_params; + +/* Real NUS 128-bit UUIDs in little-endian byte order. + * These match the standard Nordic UART Service UUIDs: + * Service: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E + * TX: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E + * RX: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E + */ +static struct bt_uuid_128 nus_service_uuid = BT_UUID_INIT_128( + 0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, + 0x93, 0xF3, 0xA3, 0xB5, 0x01, 0x00, 0x40, 0x6E); +static struct bt_uuid_128 nus_tx_uuid = BT_UUID_INIT_128( + 0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, + 0x93, 0xF3, 0xA3, 0xB5, 0x03, 0x00, 0x40, 0x6E); +static struct bt_uuid_128 nus_rx_uuid = BT_UUID_INIT_128( + 0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, + 0x93, 0xF3, 0xA3, 0xB5, 0x02, 0x00, 0x40, 0x6E); + +static uint16_t tx_handle; +static uint16_t rx_handle; +static bool tx_notify_enabled; +static bool rx_written; +static bool received; +static uint8_t received_data[32]; +static uint16_t received_len; + +static void start_scan(void); + +/* Check if advertisement data contains a name */ +static bool ad_has_name(struct net_buf_simple *ad, const char *name) { + size_t name_len = strlen(name); + + while (ad->len > 1) { + uint8_t field_len = net_buf_simple_pull_u8(ad); + if (field_len == 0 || field_len > ad->len) { + break; + } + uint8_t type = net_buf_simple_pull_u8(ad); + field_len--; + + if ((type == BT_DATA_NAME_COMPLETE || type == BT_DATA_NAME_SHORTENED) && + field_len == name_len && + memcmp(ad->data, name, name_len) == 0) { + return true; + } + net_buf_simple_pull(ad, field_len); + } + return false; +} + +static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, + struct net_buf_simple *ad) { + if (default_conn) { + return; + } + + /* Only interested in connectable devices */ + if (type != BT_GAP_ADV_TYPE_ADV_IND && + type != BT_GAP_ADV_TYPE_ADV_DIRECT_IND) { + return; + } + + /* Clone ad data so we can parse it */ + struct net_buf_simple ad_copy; + uint8_t ad_buf[64]; + size_t copy_len = ad->len < sizeof(ad_buf) ? ad->len : sizeof(ad_buf); + memcpy(ad_buf, ad->data, copy_len); + net_buf_simple_init_with_data(&ad_copy, ad_buf, copy_len); + + if (!ad_has_name(&ad_copy, "CPNUS")) { + return; + } + + char addr_str[BT_ADDR_LE_STR_LEN]; + bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); + printk("Found CPNUS device: %s (RSSI %d)\n", addr_str, rssi); + + if (bt_le_scan_stop()) { + return; + } + + int err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, + BT_LE_CONN_PARAM_DEFAULT, &default_conn); + if (err) { + printk("Create conn failed (%d)\n", err); + start_scan(); + } +} + +static void start_scan(void) { + int err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found); + if (err) { + printk("Scanning failed to start (err %d)\n", err); + return; + } + printk("Scanning started\n"); +} + +/* TX notification callback */ +static uint8_t on_tx_notify(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params, + const void *data, uint16_t length) { + if (data) { + memcpy(received_data, data, length < sizeof(received_data) ? + length : sizeof(received_data)); + received_len = length; + received = true; + printk("NUS: received '"); + for (uint16_t i = 0; i < received_len; i++) { + printk("%c", received_data[i]); + } + printk("'\n"); + + /* Disconnect after receiving response */ + bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + } + return BT_GATT_ITER_CONTINUE; +} + +static struct bt_gatt_subscribe_params tx_subscribe_params; + +/* Step 5: Subscribe to TX notifications */ +static void subscribe_tx(struct bt_conn *conn) { + tx_subscribe_params.notify = on_tx_notify; + tx_subscribe_params.value = BT_GATT_CCC_NOTIFY; + tx_subscribe_params.value_handle = tx_handle; + tx_subscribe_params.ccc_handle = tx_handle + 1; + + int err = bt_gatt_subscribe(conn, &tx_subscribe_params); + if (err) { + printk("TX subscribe failed (err %d)\n", err); + bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + } else { + tx_notify_enabled = true; + printk("TX notify enabled\n"); + } +} + +/* Step 4: Write to RX characteristic (central→peripheral) using write-without-response */ +static void write_rx(struct bt_conn *conn) { + static uint8_t data[] = "Hello"; + + int err = bt_gatt_write_without_response(conn, rx_handle, + data, 5, false); + if (err) { + printk("RX write request failed (err %d)\n", err); + bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + return; + } + rx_written = true; + printk("RX write complete\n"); + + /* Now subscribe to TX to receive the response */ + subscribe_tx(conn); +} + +/* Step 3: Discover characteristics to find TX and RX handles */ +static uint8_t discover_char_cb(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params) { + if (!attr) { + printk("Characteristic discovery complete\n"); + if (rx_handle == 0 || tx_handle == 0) { + printk("NUS characteristics not found\n"); + bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + return BT_GATT_ITER_STOP; + } + if (rx_handle) { + printk("Writing to RX...\n"); + write_rx(conn); + } + return BT_GATT_ITER_STOP; + } + + struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data; + + if (bt_uuid_cmp(chrc->uuid, &nus_rx_uuid.uuid) == 0) { + rx_handle = chrc->value_handle; + printk("Found NUS RX, handle: %u\n", rx_handle); + } else if (bt_uuid_cmp(chrc->uuid, &nus_tx_uuid.uuid) == 0) { + tx_handle = chrc->value_handle; + printk("Found NUS TX, handle: %u\n", tx_handle); + } + + return BT_GATT_ITER_CONTINUE; +} + +/* Step 2: Discover characteristics within the NUS service */ +static uint8_t discover_service_cb(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params) { + if (!attr) { + printk("Service discovery complete, NUS not found\n"); + bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + return BT_GATT_ITER_STOP; + } + + printk("Found NUS, handle: %u\n", attr->handle); + + /* Discover characteristics within this service */ + discover_params.uuid = NULL; + discover_params.start_handle = attr->handle + 1; + discover_params.end_handle = 0xFFFF; + discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; + discover_params.func = discover_char_cb; + + int err = bt_gatt_discover(conn, &discover_params); + if (err) { + printk("Char discovery failed (err %d)\n", err); + } + + return BT_GATT_ITER_STOP; +} + +/* Step 1: Connected - discover NUS */ +static void connected(struct bt_conn *conn, uint8_t err) { + char addr[BT_ADDR_LE_STR_LEN]; + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + if (err) { + printk("Failed to connect to %s (%u)\n", addr, err); + bt_conn_unref(default_conn); + default_conn = NULL; + start_scan(); + return; + } + + if (conn != default_conn) { + return; + } + + printk("Connected: %s\n", addr); + + /* Discover NUS */ + discover_params.uuid = &nus_service_uuid.uuid; + discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE; + discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE; + discover_params.type = BT_GATT_DISCOVER_PRIMARY; + discover_params.func = discover_service_cb; + + err = bt_gatt_discover(conn, &discover_params); + if (err) { + printk("Service discovery failed (err %d)\n", err); + } +} + +static void disconnected(struct bt_conn *conn, uint8_t reason) { + char addr[BT_ADDR_LE_STR_LEN]; + + if (conn != default_conn) { + return; + } + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + printk("Disconnected: %s (reason 0x%02x)\n", addr, reason); + + bt_conn_unref(default_conn); + default_conn = NULL; +} + +BT_CONN_CB_DEFINE(conn_callbacks) = { + .connected = connected, + .disconnected = disconnected, +}; + +int main(void) { + int err = bt_enable(NULL); + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return 0; + } + + printk("Bluetooth initialized\n"); + start_scan(); + return 0; +} diff --git a/ports/zephyr-cp/tests/bsim/samples/observer/CMakeLists.txt b/ports/zephyr-cp/tests/bsim/samples/observer/CMakeLists.txt new file mode 100644 index 00000000000..59d999d7052 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/samples/observer/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(observer) + +target_sources(app PRIVATE + src/main.c + src/observer.c +) diff --git a/ports/zephyr-cp/tests/bsim/samples/observer/prj.conf b/ports/zephyr-cp/tests/bsim/samples/observer/prj.conf new file mode 100644 index 00000000000..a328b3700a9 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/samples/observer/prj.conf @@ -0,0 +1,2 @@ +CONFIG_BT=y +CONFIG_BT_OBSERVER=y diff --git a/ports/zephyr-cp/tests/bsim/samples/observer/src/main.c b/ports/zephyr-cp/tests/bsim/samples/observer/src/main.c new file mode 100644 index 00000000000..db8ce6bab95 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/samples/observer/src/main.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +int observer_start(void); + +int main(void) { + int err; + + printk("Starting Observer Demo\n"); + + /* Initialize the Bluetooth Subsystem */ + err = bt_enable(NULL); + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return 0; + } + + (void)observer_start(); + + printk("Exiting %s thread.\n", __func__); + return 0; +} diff --git a/ports/zephyr-cp/tests/bsim/samples/observer/src/observer.c b/ports/zephyr-cp/tests/bsim/samples/observer/src/observer.c new file mode 100644 index 00000000000..fc896bf593d --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/samples/observer/src/observer.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, + struct net_buf_simple *ad) { + printk("Device found: %s (RSSI %d), type %u, AD data len %u\n", + bt_addr_le_str(addr), rssi, type, ad->len); +} + +#if defined(CONFIG_BT_EXT_ADV) +#define NAME_LEN 30 + +static bool data_cb(struct bt_data *data, void *user_data) { + char *name = user_data; + uint8_t len; + + switch (data->type) { + case BT_DATA_NAME_SHORTENED: + case BT_DATA_NAME_COMPLETE: + len = MIN(data->data_len, NAME_LEN - 1); + (void)memcpy(name, data->data, len); + name[len] = '\0'; + return false; + default: + return true; + } +} + +static const char *phy2str(uint8_t phy) { + switch (phy) { + case BT_GAP_LE_PHY_NONE: + return "No packets"; + case BT_GAP_LE_PHY_1M: + return "LE 1M"; + case BT_GAP_LE_PHY_2M: + return "LE 2M"; + case BT_GAP_LE_PHY_CODED: + return "LE Coded"; + default: + return "Unknown"; + } +} + +static void scan_recv(const struct bt_le_scan_recv_info *info, + struct net_buf_simple *buf) { + char name[NAME_LEN]; + uint8_t data_status; + uint16_t data_len; + + (void)memset(name, 0, sizeof(name)); + + data_len = buf->len; + bt_data_parse(buf, data_cb, name); + + data_status = BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS(info->adv_props); + + printk("[DEVICE]: %s, AD evt type %u, Tx Pwr: %i, RSSI %i " + "Data status: %u, AD data len: %u Name: %s " + "C:%u S:%u D:%u SR:%u E:%u Pri PHY: %s, Sec PHY: %s, " + "Interval: 0x%04x (%u ms), SID: %u\n", + bt_addr_le_str(info->addr), info->adv_type, info->tx_power, info->rssi, + data_status, data_len, name, + (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0, + (info->adv_props & BT_GAP_ADV_PROP_SCANNABLE) != 0, + (info->adv_props & BT_GAP_ADV_PROP_DIRECTED) != 0, + (info->adv_props & BT_GAP_ADV_PROP_SCAN_RESPONSE) != 0, + (info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) != 0, + phy2str(info->primary_phy), phy2str(info->secondary_phy), + info->interval, info->interval * 5 / 4, info->sid); +} + +static struct bt_le_scan_cb scan_callbacks = { + .recv = scan_recv, +}; +#endif /* CONFIG_BT_EXT_ADV */ + +static int scan_start(void) { + /* 30 ms continuous active scanning. Duplicate filtering is disabled so + * that updated advertisements from the same device (e.g. when user + * code replaces the workflow advert) are reported again. + */ + struct bt_le_scan_param scan_param = { + .type = BT_LE_SCAN_TYPE_ACTIVE, + .options = 0, + .interval = BT_GAP_SCAN_FAST_INTERVAL_MIN, + .window = BT_GAP_SCAN_FAST_WINDOW, + }; + int err; + + #if defined(CONFIG_BT_EXT_ADV) + scan_param.options |= BT_LE_SCAN_OPT_CODED; + #endif /* CONFIG_BT_EXT_ADV */ + +scan_start_retry: + printk("Starting scanning...\n"); + err = bt_le_scan_start(&scan_param, device_found); + if (err) { + if ((scan_param.options & BT_LE_SCAN_OPT_CODED) != 0U) { + printk("Failed to start scanning with Coded PHY (err %d), retrying " + "without...\n", err); + + scan_param.options &= ~BT_LE_SCAN_OPT_CODED; + + goto scan_start_retry; + } + + printk("Start scanning failed (err %d)\n", err); + + return err; + } + + printk("success.\n"); + + return 0; +} + +int observer_start(void) { + int err; + + #if defined(CONFIG_BT_EXT_ADV) + bt_le_scan_cb_register(&scan_callbacks); + printk("Registered scan callbacks\n"); + #endif /* CONFIG_BT_EXT_ADV */ + + err = scan_start(); + if (err != 0) { + return err; + } + + return 0; +} diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_basics.py b/ports/zephyr-cp/tests/bsim/test_bsim_basics.py index 477292ddd54..12165750274 100644 --- a/ports/zephyr-cp/tests/bsim/test_bsim_basics.py +++ b/ports/zephyr-cp/tests/bsim/test_bsim_basics.py @@ -1,11 +1,11 @@ # SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries # SPDX-License-Identifier: MIT -"""Basic BabbleSim connectivity tests for nrf5340bsim.""" +"""Basic BabbleSim connectivity tests for bsim.""" import pytest -pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") +from .conftest import get_library_files BSIM_CODE = """\ print("bsim ready") @@ -14,8 +14,8 @@ @pytest.mark.circuitpy_drive({"code.py": BSIM_CODE}) @pytest.mark.circuitpy_drive({"code.py": BSIM_CODE}) -@pytest.mark.duration(3) -def test_bsim_dual_instance_connect(bsim_phy, circuitpython1, circuitpython2): +@pytest.mark.duration(20) +def test_bsim_dual_instance_connect(bsim_phy, circuitpython1, circuitpython2, board): """Run two bsim instances on the same sim id and verify UART output.""" # Wait for both devices to complete before checking output. @@ -25,7 +25,31 @@ def test_bsim_dual_instance_connect(bsim_phy, circuitpython1, circuitpython2): output0 = circuitpython1.serial.all_output output1 = circuitpython2.serial.all_output - assert "Board ID:native_nrf5340bsim" in output0 - assert "Board ID:native_nrf5340bsim" in output1 + assert f"Board ID:{board}" in output0 + assert f"Board ID:{board}" in output1 assert "bsim ready" in output0 assert "bsim ready" in output1 + + +# --- adafruit_ble library import --- + +BSIM_BLE_IMPORT_CODE = """\ +import adafruit_ble + +print("adafruit_ble version", adafruit_ble.__version__) +print("adafruit_ble repo", adafruit_ble.__repo__) +print("done") +""" + + +@pytest.mark.circuitpy_drive( + {"code.py": BSIM_BLE_IMPORT_CODE, **get_library_files("adafruit_ble")} +) +def test_bsim_ble_library_import(bsim_phy, circuitpython): + """Import adafruit_ble from CIRCUITPY and verify basic attributes.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "adafruit_ble version" in output + assert "adafruit_ble repo" in output + assert "done" in output diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_adapter.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_adapter.py new file mode 100644 index 00000000000..5a15bb03d74 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_adapter.py @@ -0,0 +1,140 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""BLE adapter state tests for bsim.""" + +import pytest + + +# --- Enable/disable cycle --- + +BSIM_ENABLE_DISABLE_CODE = """\ +import _bleio + +adapter = _bleio.adapter + +# Check initial state (should be enabled after boot) +print("enabled start", adapter.enabled) + +# Disable +adapter.enabled = False +print("enabled false", adapter.enabled) + +# Re-enable +adapter.enabled = True +print("enabled true", adapter.enabled) + +# Verify it reports as enabled +print("enabled final", adapter.enabled) +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_ENABLE_DISABLE_CODE}) +def test_bsim_adapter_enable_disable(bsim_phy, circuitpython): + """Toggle adapter.enabled and verify state.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "enabled start True" in output + assert "enabled false False" in output + assert "enabled true True" in output + assert "enabled final True" in output + assert "done" in output + + +# --- Disable stops advertising --- + +BSIM_DISABLE_STOPS_ADV_CODE = """\ +import _bleio + +adapter = _bleio.adapter + +name = b"CPADV" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name + +adapter.start_advertising(advertisement, connectable=False) +print("advertising", adapter.advertising) + +adapter.enabled = False +print("advertising after disable", adapter.advertising) + +adapter.enabled = True +print("advertising after enable", adapter.advertising) +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_DISABLE_STOPS_ADV_CODE}) +def test_bsim_adapter_disable_stops_advertising(bsim_phy, circuitpython): + """Disabling the adapter stops advertising.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "advertising True" in output + assert "advertising after disable False" in output + assert "advertising after enable False" in output + assert "done" in output + + +# --- Adapter state across soft reload --- + +BSIM_ENABLE_RELOAD_CODE = """\ +import _bleio + +adapter = _bleio.adapter + +print("enabled", adapter.enabled) +print("advertising", adapter.advertising) +print("connected", adapter.connected) +print("done") +""" + + +@pytest.mark.code_py_runs(2) +@pytest.mark.circuitpy_drive({"code.py": BSIM_ENABLE_RELOAD_CODE}) +def test_bsim_adapter_state_after_reload(bsim_phy, circuitpython): + """Adapter state is clean after soft reload.""" + circuitpython.serial.wait_for("done") + circuitpython.serial.wait_for("Press any key to enter the REPL") + circuitpython.serial.write("\x04") + + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert output.count("enabled True") >= 2 + assert output.count("advertising False") >= 2 + assert output.count("connected False") >= 2 + + +# --- Adapter name truncation --- + +BSIM_NAME_TRUNCATION_CODE = """\ +import _bleio + +adapter = _bleio.adapter + +# Set a very long name +long_name = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%" +adapter.name = long_name + +# Read back — should be truncated to fit CONFIG_BT_DEVICE_NAME_MAX +name = adapter.name +print("name len", len(name)) +print("name", name) +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_NAME_TRUNCATION_CODE}) +def test_bsim_adapter_name_truncation(bsim_phy, circuitpython): + """Very long adapter.name is truncated to fit Zephyr limit.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "name len" in output + # Should be truncated — length must be less than input (72 chars) + name_line = [l for l in output.split("\n") if "name len" in l][0] + name_len = int(name_line.split()[-1]) + assert name_len < 72 + assert "done" in output diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_advertising.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_advertising.py index 33680fe2506..c8450e2af9f 100644 --- a/ports/zephyr-cp/tests/bsim/test_bsim_ble_advertising.py +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_advertising.py @@ -1,14 +1,13 @@ # SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries # SPDX-License-Identifier: MIT -"""BLE advertising tests for nrf5340bsim.""" +"""BLE advertising tests for bsim.""" import logging import re import pytest -pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") logger = logging.getLogger(__name__) @@ -78,8 +77,58 @@ """ -@pytest.mark.zephyr_sample("bluetooth/observer") +# Advertiser: primary advert carries a distinctive marker; the scan response +# carries a different marker. If the adapter merges them they'd arrive as one +# entry, but the adapter does not merge — they come as separate ScanEntry +# objects distinguished by `scan_response`. +BSIM_ADVERT_SCAN_RESPONSE_ADV_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +name = b"ADVADV" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name + +sname = b"SCANRSP" +scan_response = bytes((len(sname) + 1, 0x09)) + sname + +print("adv start") +adapter.start_advertising(advertisement, scan_response=scan_response, connectable=True) +time.sleep(10) +adapter.stop_advertising() +print("adv done") +""" + +BSIM_ADVERT_SCAN_RESPONSE_SCAN_CODE = """\ +import _bleio + +adapter = _bleio.adapter +print("scan start") +saw_primary = False +saw_scan_response = False +for entry in adapter.start_scan(active=True, timeout=12.0): + data = entry.advertisement_bytes + if b"ADVADV" in data and not entry.scan_response: + saw_primary = True + print("primary") + if b"SCANRSP" in data and entry.scan_response: + saw_scan_response = True + print("scan_response") + if saw_primary and saw_scan_response: + print("both") + break +adapter.stop_scan() +print("scan done", saw_primary, saw_scan_response) +""" + +# Disable the workflow on the advertiser so only its custom advert is on air. +BSIM_ADVERT_SCAN_RESPONSE_SETTINGS = "CIRCUITPY_BLE_WORKFLOW = false\n" + + +@pytest.mark.zephyr_sample("tests/bsim/samples/observer") @pytest.mark.circuitpy_drive({"code.py": BSIM_ADV_CODE}) +@pytest.mark.duration(60) def test_bsim_advertise_and_scan(bsim_phy, circuitpython, zephyr_sample): """Advertise from CircuitPython and verify Zephyr observer sees traffic.""" observer = zephyr_sample @@ -95,9 +144,9 @@ def test_bsim_advertise_and_scan(bsim_phy, circuitpython, zephyr_sample): assert "AD data len 10" in observer_output -@pytest.mark.zephyr_sample("bluetooth/observer") +@pytest.mark.zephyr_sample("tests/bsim/samples/observer") @pytest.mark.code_py_runs(2) -@pytest.mark.duration(25) +@pytest.mark.duration(60) @pytest.mark.circuitpy_drive({"code.py": BSIM_ADV_INTERRUPT_RELOAD_CODE}) def test_bsim_advertise_ctrl_c_reload(bsim_phy, circuitpython, zephyr_sample): """Ensure advertising resumes after Ctrl-C and a reload.""" @@ -108,7 +157,9 @@ def test_bsim_advertise_ctrl_c_reload(bsim_phy, circuitpython, zephyr_sample): observer_count_before = observer.serial.all_output.count("Device found:") circuitpython.serial.write("\x03") - circuitpython.serial.wait_for("KeyboardInterrupt") + # Real AES for the link layer adds CPU overhead that can delay the + # interpreter noticing SIGINT beyond the default wait_for timeout. + circuitpython.serial.wait_for("KeyboardInterrupt", timeout=20) circuitpython.serial.write("\x04") circuitpython.wait_until_done() @@ -126,9 +177,10 @@ def test_bsim_advertise_ctrl_c_reload(bsim_phy, circuitpython, zephyr_sample): assert "Already advertising" not in cp_output -@pytest.mark.zephyr_sample("bluetooth/observer") +@pytest.mark.zephyr_sample("tests/bsim/samples/observer") +@pytest.mark.duration(40) @pytest.mark.circuitpy_drive({"code.py": BSIM_TX_POWER_DEFAULT_CODE}) -def test_bsim_tx_power_default_rssi(bsim_phy, circuitpython, zephyr_sample): +def test_bsim_tx_power_default_rssi(board, bsim_phy, circuitpython, zephyr_sample): """Verify default TX power produces expected RSSI.""" observer = zephyr_sample @@ -142,16 +194,18 @@ def test_bsim_tx_power_default_rssi(bsim_phy, circuitpython, zephyr_sample): # Observer: "Device found: (RSSI ), type , AD data len " # Advertisement is 12 bytes: flags (3) + name (9). - # With 40 dB channel attenuation and 0 dBm TX → RSSI ~ -39 + # With 40 dB channel attenuation and 0 dBm TX → RSSI ~ -39. + expected_rssi = -39 rssi_pattern = re.compile(r"RSSI (-?\d+)\), type \d+, AD data len 12") all_rssi = [int(m.group(1)) for m in rssi_pattern.finditer(obs_output)] logger.info("RSSI values: %s", all_rssi) assert len(all_rssi) > 0, "Observer saw no advertisements" - assert all_rssi[0] == -39, f"Expected RSSI -39 (0 dBm TX), got {all_rssi[0]}" + assert all_rssi[0] == expected_rssi, f"Expected RSSI {expected_rssi}, got {all_rssi[0]}" -@pytest.mark.zephyr_sample("bluetooth/observer") +@pytest.mark.zephyr_sample("tests/bsim/samples/observer") +@pytest.mark.duration(40) @pytest.mark.circuitpy_drive({"code.py": BSIM_TX_POWER_LOW_CODE}) def test_bsim_tx_power_low_rssi(bsim_phy, circuitpython, zephyr_sample): """Verify low TX power reduces RSSI.""" @@ -172,3 +226,37 @@ def test_bsim_tx_power_low_rssi(bsim_phy, circuitpython, zephyr_sample): assert len(all_rssi) > 0, "Observer saw no advertisements" assert all_rssi[0] < -39, f"Expected lower RSSI with -20 dBm TX, got {all_rssi[0]}" + + +@pytest.mark.duration(20) +@pytest.mark.circuitpy_drive( + { + "code.py": BSIM_ADVERT_SCAN_RESPONSE_ADV_CODE, + "settings.toml": BSIM_ADVERT_SCAN_RESPONSE_SETTINGS, + } +) +@pytest.mark.circuitpy_drive({"code.py": BSIM_ADVERT_SCAN_RESPONSE_SCAN_CODE}) +def test_bsim_scan_response_not_merged(bsim_phy, circuitpython1, circuitpython2): + """The adapter does not merge advertisement and scan response. + + The advertiser sends a primary advert (marker "ADVADV") and a scan + response (marker "SCANRSP") as separate packets. With duplicate filtering + disabled and no merging in the adapter, the scanner observes them as two + distinct ScanEntry objects: one with scan_response=False (the primary + advert) and one with scan_response=True (the scan response). + """ + scanner = circuitpython2 + + scanner.wait_until_done() + + scanner_output = scanner.serial.all_output + assert "primary" in scanner_output, ( + f"primary advert entry not observed separately: {scanner_output}" + ) + assert "scan_response" in scanner_output, ( + f"scan response entry not observed separately: {scanner_output}" + ) + assert "both" in scanner_output, ( + f"did not observe both advert and scan response: {scanner_output}" + ) + assert "scan done True True" in scanner_output, f"scan did not confirm both: {scanner_output}" diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_connect.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_connect.py index 21cfeaf79da..8d3c1f2594a 100644 --- a/ports/zephyr-cp/tests/bsim/test_bsim_ble_connect.py +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_connect.py @@ -1,11 +1,10 @@ # SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries # SPDX-License-Identifier: MIT -"""BLE central connection tests for nrf5340bsim.""" +"""BLE central connection tests for bsim.""" import pytest -pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") BSIM_CONNECT_CODE = """\ import _bleio diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_descriptor.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_descriptor.py new file mode 100644 index 00000000000..6d5cf418147 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_descriptor.py @@ -0,0 +1,131 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""BLE descriptor tests for bsim.""" + +import pytest + +# =================================================================== +# Server: characteristic with user_description +# =================================================================== + +BSIM_DESC_SERVER_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +svc = _bleio.Service(_bleio.UUID(0x180F)) +char = _bleio.Characteristic.add_to_service( + svc, _bleio.UUID(0x2A19), + properties=_bleio.Characteristic.READ, + read_perm=_bleio.Attribute.OPEN, + write_perm=_bleio.Attribute.NO_ACCESS, + max_length=1, fixed_length=True, initial_value=bytes([75]), + user_description="Battery Level", +) +print("service created") + +# Check local descriptors list +descs = char.descriptors +print("num descriptors", len(descs)) +for d in descs: + print("desc uuid", d.uuid.uuid16) + print("desc value", list(d.value)) + +name = b"CPDESC" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name +adapter.start_advertising(advertisement, connectable=True) + +for _ in range(80): + if adapter.connected: + break + time.sleep(0.1) +print("connected", adapter.connected) + +for _ in range(80): + if not adapter.connected: + break + time.sleep(0.1) +print("done") +""" + +# =================================================================== +# Client: connects and reads the remote user description +# =================================================================== + +BSIM_DESC_CLIENT_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +print("client start") +target = None +for entry in adapter.start_scan(timeout=6.0, active=True): + if entry.connectable and b"CPDESC" in entry.advertisement_bytes: + target = entry.address + print("found server") + break +adapter.stop_scan() +print("have target", target is not None) + +if target is None: + raise RuntimeError("No server found") + +connection = adapter.connect(target, timeout=5.0) +print("connected", connection.connected) + +services = connection.discover_remote_services([_bleio.UUID(0x180F)]) +char = services[0].characteristics[0] + +print("char value", list(char.value)) + +descs = char.descriptors +print("num descriptors", len(descs)) +for d in descs: + print("desc uuid", hex(d.uuid.uuid16)) + print("desc value", list(d.value)) + +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while connection.connected and time.monotonic() < timeout: + time.sleep(0.1) +print("done") +""" + + +@pytest.mark.duration(30) +@pytest.mark.circuitpy_drive({"code.py": BSIM_DESC_SERVER_CODE}) +@pytest.mark.circuitpy_drive({"code.py": BSIM_DESC_CLIENT_CODE}) +def test_bsim_descriptor_user_description(bsim_phy, circuitpython1, circuitpython2): + """Server creates a characteristic with user_description; + client discovers the service and reads the descriptor.""" + server = circuitpython1 + client = circuitpython2 + + client.wait_until_done() + server.wait_until_done() + + server_output = server.serial.all_output + client_output = client.serial.all_output + + # Server: user_description creates a CUD descriptor (0x2901) on the characteristic + assert "service created" in server_output + assert "num descriptors 1" in server_output + assert "desc uuid 10497" in server_output # 0x2901 + assert ( + "desc value [66, 97, 116, 116, 101, 114, 121, 32, 76, 101, 118, 101, 108]" in server_output + ) + assert "connected True" in server_output + + # Client: discovers and reads the remote descriptor + assert "found server" in client_output + assert "char value [75]" in client_output + assert "num descriptors 1" in client_output + assert "desc uuid 0x2901" in client_output + assert ( + "desc value [66, 97, 116, 116, 101, 114, 121, 32, 76, 101, 118, 101, 108]" in client_output + ) + assert "done" in client_output diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_name.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_name.py index 69435d38256..b64fb2125f1 100644 --- a/ports/zephyr-cp/tests/bsim/test_bsim_ble_name.py +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_name.py @@ -1,25 +1,126 @@ # SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries # SPDX-License-Identifier: MIT -"""BLE name tests for nrf5340bsim.""" +"""BLE adapter name tests for bsim. + +Covers reading the default adapter name (derived from the device address), +setting a custom name and reading it back, and verifying the default name is +re-applied every time the adapter is re-enabled. +""" + +import re import pytest -pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") -BSIM_NAME_CODE = """\ +# The address prints as
in big-endian/expected order. +# The default name is "CIRCUITPY" plus the last two octets (ee, ff) concatenated +# without colons, e.g. an address of
-> "CIRCUITPYd224". +ADDRESS_RE = re.compile( + r"
" +) + +# Strip ANSI/OSC escape sequences (e.g. terminal title updates) and carriage +# returns that CircuitPython interleaves with code.py output. +_ANSI_RE = re.compile(r"\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b\[[0-9;?]*[A-Za-z]") + + +def _clean(output: str) -> str: + return _ANSI_RE.sub("", output).replace("\r", "") + + +def _expected_default_name(address_repr: str) -> str: + m = ADDRESS_RE.search(address_repr) + assert m is not None, f"unexpected address repr: {address_repr!r}" + return "CIRCUITPY" + m.group(5) + m.group(6) + + +# --- Default name is derived from the adapter address --- + +BSIM_DEFAULT_NAME_CODE = """\ +import _bleio + +adapter = _bleio.adapter +adapter.enabled = True +print("default name", adapter.name) +print("address", adapter.address) +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_DEFAULT_NAME_CODE}) +def test_bsim_default_name_matches_address(bsim_phy, circuitpython): + """The default name is CIRCUITPY plus the last two address octets.""" + circuitpython.wait_until_done() + + output = _clean(circuitpython.serial.all_output) + name = re.search(r"(?m)^default name (\S+)$", output).group(1) + address_repr = re.search(r"(?m)^address (.+)$", output).group(1).strip() + + assert name == _expected_default_name(address_repr) + assert re.search(r"(?m)^done$", output) is not None + + +# --- Set a custom name and read it back --- + +BSIM_SET_NAME_CODE = """\ import _bleio adapter = _bleio.adapter adapter.enabled = True adapter.name = "CPNAME" print("name", adapter.name) +print("done") """ -@pytest.mark.circuitpy_drive({"code.py": BSIM_NAME_CODE}) +@pytest.mark.circuitpy_drive({"code.py": BSIM_SET_NAME_CODE}) def test_bsim_set_name(bsim_phy, circuitpython): """Set the BLE name and read it back on bsim.""" circuitpython.wait_until_done() - assert "name CPNAME" in circuitpython.serial.all_output + output = _clean(circuitpython.serial.all_output) + assert re.search(r"(?m)^name CPNAME$", output) is not None + assert re.search(r"(?m)^done$", output) is not None + + +# --- Re-enabling the adapter restores the default name --- + +BSIM_NAME_RESET_ON_ENABLE_CODE = """\ +import _bleio + +adapter = _bleio.adapter + +adapter.enabled = True +print("default", adapter.name) + +adapter.name = "CUSTOM" +print("custom", adapter.name) + +# Re-enabling re-applies the default name derived from the address. +adapter.enabled = False +adapter.enabled = True +print("reset", adapter.name) +print("address", adapter.address) +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_NAME_RESET_ON_ENABLE_CODE}) +def test_bsim_name_resets_on_enable(bsim_phy, circuitpython): + """Disabling and re-enabling the adapter restores the default name.""" + circuitpython.wait_until_done() + + output = _clean(circuitpython.serial.all_output) + default_name = re.search(r"(?m)^default (\S+)$", output).group(1) + assert re.search(r"(?m)^custom CUSTOM$", output) is not None + reset_name = re.search(r"(?m)^reset (\S+)$", output).group(1) + address_repr = re.search(r"(?m)^address (.+)$", output).group(1).strip() + + # The custom name is gone and the address-derived default is back. + assert reset_name != "CUSTOM" + assert reset_name == _expected_default_name(address_repr) + # The re-applied default matches the boot-time default. + assert reset_name == default_name + assert re.search(r"(?m)^done$", output) is not None diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_nus.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_nus.py new file mode 100644 index 00000000000..1336d37fbd0 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_nus.py @@ -0,0 +1,183 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Nordic UART Service (NUS) tests for bsim — using adafruit_ble library.""" + +import pytest + +from .conftest import get_library_files + +_ADAFRUIT_BLE = get_library_files("adafruit_ble") + +# ---- Test 1: CP peripheral hosts NUS, Zephyr central writes and reads ---- + +BSIM_NUS_PERIPHERAL_CODE = """\ +import time +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from adafruit_ble.services.nordic import UARTService + +ble = BLERadio() +uart = UARTService() +print("service created") + +advertisement = ProvideServicesAdvertisement(uart) +advertisement.complete_name = "CPNUS" +ble.start_advertising(advertisement) +print("advertising") + +for _ in range(80): + if ble.connected: + break + time.sleep(0.1) +print("connected", ble.connected) + +# Wait for data to arrive from central +data = uart.read(5) +print("received", data) + +# Send a response back +uart.write(b"World") +print("sent response") + +time.sleep(1.0) + +for _ in range(80): + if not ble.connected: + break + time.sleep(0.1) +print("done") +""" + + +@pytest.mark.zephyr_sample("tests/bsim/samples/central_nus_client") +@pytest.mark.duration(14) +@pytest.mark.circuitpy_drive( + { + "code.py": BSIM_NUS_PERIPHERAL_CODE, + "settings.toml": "CIRCUITPY_BLE_WORKFLOW = false\n", + **_ADAFRUIT_BLE, + } +) +def test_bsim_nus_peripheral(bsim_phy, circuitpython, zephyr_sample): + """CP hosts NUS peripheral; Zephyr central writes to RX, reads TX notifications.""" + circuitpython.wait_until_done() + + cp_output = circuitpython.serial.all_output + sample_output = zephyr_sample.serial.all_output + + assert "service created" in cp_output + assert "connected True" in cp_output + assert "received" in cp_output + assert "sent response" in cp_output + assert "NUS: received 'World'" in sample_output + + +# ---- Test 2: CP-to-CP NUS (peripheral + central) ---- + +BSIM_NUS_SERVER_CODE = """\ +import time +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from adafruit_ble.services.nordic import UARTService + +ble = BLERadio() +uart = UARTService() +print("service created") + +advertisement = ProvideServicesAdvertisement(uart) +advertisement.complete_name = "CP2CP" +ble.start_advertising(advertisement) +print("advertising") + +for _ in range(80): + if ble.connected: + break + time.sleep(0.1) +print("connected", ble.connected) + +# Read incoming data from central +data = uart.read(6) +print("received", data) + +# Respond back +uart.write(b"OK!") +print("sent response") + +for _ in range(80): + if not ble.connected: + break + time.sleep(0.1) +print("done") +""" + +BSIM_NUS_CLIENT_CODE = """\ +import time +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from adafruit_ble.services.nordic import UARTService + +ble = BLERadio() + +print("client start") +target = None +for adv in ble.start_scan(ProvideServicesAdvertisement, timeout=8.0): + if adv.connectable and adv.complete_name == "CP2CP": + target = adv + print("found server") + break +ble.stop_scan() +print("have target", target is not None) + +if target is None: + raise RuntimeError("No server found") + +connection = ble.connect(target, timeout=5.0) +print("connected", connection.connected) + +uart = connection[UARTService] +print("discovered services", 1) + +# Write to server +uart.write(b"Hello!") +print("wrote to rx") + +data = uart.read(3) +print("received", data) + +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while connection.connected and time.monotonic() < timeout: + time.sleep(0.1) +print("done") +""" + + +@pytest.mark.duration(30) +@pytest.mark.circuitpy_drive({"code.py": BSIM_NUS_SERVER_CODE, **_ADAFRUIT_BLE}) +@pytest.mark.circuitpy_drive({"code.py": BSIM_NUS_CLIENT_CODE, **_ADAFRUIT_BLE}) +def test_bsim_nus_cp_to_cp(bsim_phy, circuitpython1, circuitpython2): + """CP peripheral hosts NUS; CP central writes to RX, reads TX via notifications.""" + server = circuitpython1 + client = circuitpython2 + + client.wait_until_done() + server.wait_until_done() + + server_output = server.serial.all_output + client_output = client.serial.all_output + + assert "service created" in server_output + assert "connected True" in server_output + assert "received" in server_output + assert "sent response" in server_output + + assert "client start" in client_output + assert "found server" in client_output + assert "have target True" in client_output + assert "connected True" in client_output + assert "discovered services 1" in client_output + assert "wrote to rx" in client_output + assert "received b'OK!'" in client_output + assert "done" in client_output diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_packet_buffer.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_packet_buffer.py new file mode 100644 index 00000000000..27bb703f379 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_packet_buffer.py @@ -0,0 +1,1141 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""PacketBuffer tests for bsim.""" + +import re + +import pytest + + +# ---- Test 1: Server-side PacketBuffer incoming (WRITE characteristic) ---- + +BSIM_PB_SERVER_IN_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +svc = _bleio.Service(_bleio.UUID(0xFFE0)) +char = _bleio.Characteristic.add_to_service( + svc, _bleio.UUID(0xFFE1), + properties=_bleio.Characteristic.WRITE, + read_perm=_bleio.Attribute.NO_ACCESS, + write_perm=_bleio.Attribute.OPEN, + max_length=20, fixed_length=False, +) + +# Wrap in PacketBuffer for incoming packets +pb = _bleio.PacketBuffer(char, buffer_size=4, max_packet_size=20) +print("service created") + +name = b"CPPBIN" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name +adapter.start_advertising(advertisement, connectable=True) +print("advertising") + +for _ in range(80): + if adapter.connected: + break + time.sleep(0.1) +print("connected", adapter.connected) + +# Read first incoming packet +data = bytearray(20) +n = 0 +deadline = time.monotonic() + 5.0 +while n == 0 and time.monotonic() < deadline: + n = pb.readinto(data) + if n == 0: + time.sleep(0.05) +print("packet1", data[:n]) + +# Read second incoming packet +n2 = 0 +deadline2 = time.monotonic() + 5.0 +while n2 == 0 and time.monotonic() < deadline2: + n2 = pb.readinto(data) + if n2 == 0: + time.sleep(0.05) +print("packet2", data[:n2]) + +for _ in range(80): + if not adapter.connected: + break + time.sleep(0.1) +print("done") +""" + +BSIM_PB_CLIENT_IN_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +target = None +for entry in adapter.start_scan(timeout=6.0, active=True): + if entry.connectable and b"CPPBIN" in entry.advertisement_bytes: + target = entry.address + print("found server") + break +adapter.stop_scan() + +connection = adapter.connect(target, timeout=5.0) +print("connected", connection.connected) + +services = connection.discover_remote_services([_bleio.UUID(0xFFE0)]) +print("discovered services", len(services)) + +remote_char = services[0].characteristics[0] +print("found char") + +# Write two packets +remote_char.value = bytes([0x01, 0x02, 0x03, 0x04]) +print("wrote packet1") + +time.sleep(0.3) + +remote_char.value = bytes([0xAA, 0xBB, 0xCC, 0xDD, 0xEE]) +print("wrote packet2") + +time.sleep(0.5) + +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while connection.connected and time.monotonic() < timeout: + time.sleep(0.1) +print("done") +""" + + +@pytest.mark.duration(25) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PB_SERVER_IN_CODE}) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PB_CLIENT_IN_CODE}) +def test_bsim_packet_buffer_server_incoming(bsim_phy, circuitpython1, circuitpython2): + """PacketBuffer on server-side WRITE characteristic receives framed packets.""" + server = circuitpython1 + client = circuitpython2 + + client.wait_until_done() + server.wait_until_done() + + server_output = server.serial.all_output + client_output = client.serial.all_output + + assert "service created" in server_output + assert "connected True" in server_output + assert "packet1" in server_output + assert "packet2" in server_output + assert "done" in server_output + + assert "found server" in client_output + assert "wrote packet1" in client_output + assert "wrote packet2" in client_output + + +# ---- Test 2: Server-side PacketBuffer write/flush (NOTIFY response) ---- + +BSIM_PB_BIDI_SERVER_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +svc = _bleio.Service(_bleio.UUID(0xFFE0)) +char = _bleio.Characteristic.add_to_service( + svc, _bleio.UUID(0xFFE1), + properties=_bleio.Characteristic.WRITE | _bleio.Characteristic.NOTIFY, + read_perm=_bleio.Attribute.NO_ACCESS, + write_perm=_bleio.Attribute.OPEN, + max_length=20, fixed_length=False, +) + +# Wrap in PacketBuffer for incoming packets and outgoing notifications +pb = _bleio.PacketBuffer(char, buffer_size=4, max_packet_size=20) +print("service created") + +name = b"CPPBBI" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name +adapter.start_advertising(advertisement, connectable=True) +print("advertising") + +for _ in range(80): + if adapter.connected: + break + time.sleep(0.1) +print("connected", adapter.connected) + +# Read incoming packet from client +data = bytearray(20) +n = 0 +deadline = time.monotonic() + 5.0 +while n == 0 and time.monotonic() < deadline: + n = pb.readinto(data) + if n == 0: + time.sleep(0.05) +print("received", data[:n]) + +# Write response back (via NOTIFY) — sends immediately via completion callback +pb.write(bytes([0x52, 0x45, 0x53, 0x50])) # "RESP" +print("sent response") + +for _ in range(80): + if not adapter.connected: + break + time.sleep(0.1) +print("done") +""" + +BSIM_PB_BIDI_CLIENT_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +target = None +for entry in adapter.start_scan(timeout=6.0, active=True): + if entry.connectable and b"CPPBBI" in entry.advertisement_bytes: + target = entry.address + print("found server") + break +adapter.stop_scan() + +connection = adapter.connect(target, timeout=5.0) +print("connected", connection.connected) + +services = connection.discover_remote_services([_bleio.UUID(0xFFE0)]) +print("discovered services", len(services)) + +remote_char = services[0].characteristics[0] +print("found char, props", remote_char.properties) + +# Write a packet to the server +remote_char.value = bytes([0x48, 0x45, 0x4C, 0x4C, 0x4F]) # "HELLO" +print("wrote hello") + +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while connection.connected and time.monotonic() < timeout: + time.sleep(0.1) +print("done") +""" + + +@pytest.mark.duration(20) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PB_BIDI_SERVER_CODE}) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PB_BIDI_CLIENT_CODE}) +def test_bsim_packet_buffer_bidirectional(bsim_phy, circuitpython1, circuitpython2): + """Bidirectional PacketBuffer: server receives WRITE, sends NOTIFY response.""" + server = circuitpython1 + client = circuitpython2 + + client.wait_until_done() + server.wait_until_done() + + server_output = server.serial.all_output + client_output = client.serial.all_output + + assert "service created" in server_output + assert "connected True" in server_output + assert "received" in server_output + assert "sent response" in server_output + assert "done" in server_output + + assert "found server" in client_output + assert "wrote hello" in client_output + assert "done" in client_output + + +# ---- Test 3: PacketBuffer with multiple queued incoming packets ---- + +BSIM_PB_QUEUE_SERVER_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +svc = _bleio.Service(_bleio.UUID(0xFFE0)) +char = _bleio.Characteristic.add_to_service( + svc, _bleio.UUID(0xFFE1), + properties=_bleio.Characteristic.WRITE, + read_perm=_bleio.Attribute.NO_ACCESS, + write_perm=_bleio.Attribute.OPEN, + max_length=20, fixed_length=False, +) + +# Small buffer: only 1 packet, small max_packet_size +pb = _bleio.PacketBuffer(char, buffer_size=1, max_packet_size=10) +print("service created") + +name = b"CPPBQU" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name +adapter.start_advertising(advertisement, connectable=True) +print("advertising") + +for _ in range(80): + if adapter.connected: + break + time.sleep(0.1) +print("connected", adapter.connected) + +# Wait for client to send all packets +time.sleep(1.0) + +# Read packets - with buffer_size=1, oldest packets are dropped +data = bytearray(20) + +n1 = pb.readinto(data) +print("pkt1", data[:n1]) + +n2 = pb.readinto(data) +print("pkt2", data[:n2]) + +for _ in range(80): + if not adapter.connected: + break + time.sleep(0.1) +print("done") +""" + +BSIM_PB_QUEUE_CLIENT_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +target = None +for entry in adapter.start_scan(timeout=6.0, active=True): + if entry.connectable and b"CPPBQU" in entry.advertisement_bytes: + target = entry.address + print("found server") + break +adapter.stop_scan() + +connection = adapter.connect(target, timeout=5.0) +print("connected", connection.connected) + +services = connection.discover_remote_services([_bleio.UUID(0xFFE0)]) +remote_char = services[0].characteristics[0] + +# Write 3 packets quickly (buffer only holds 2, so oldest should be dropped) +remote_char.value = bytes([0x01, 0x02, 0x03]) +print("wrote pkt1") + +time.sleep(0.1) + +remote_char.value = bytes([0x04, 0x05, 0x06]) +print("wrote pkt2") + +time.sleep(0.1) + +remote_char.value = bytes([0x07, 0x08, 0x09]) +print("wrote pkt3") + +time.sleep(0.5) + +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while connection.connected and time.monotonic() < timeout: + time.sleep(0.1) +print("done") +""" + + +@pytest.mark.duration(30) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PB_QUEUE_SERVER_CODE}) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PB_QUEUE_CLIENT_CODE}) +def test_bsim_packet_buffer_queue(bsim_phy, circuitpython1, circuitpython2): + """PacketBuffer queues multiple packets; oldest dropped when buffer full.""" + server = circuitpython1 + client = circuitpython2 + + client.wait_until_done() + server.wait_until_done() + + server_output = server.serial.all_output + client_output = client.serial.all_output + + assert "service created" in server_output + assert "connected True" in server_output + # With buffer_size=1, only the last packet(s) remain + assert "pkt1" in server_output + assert "pkt2" in server_output + assert "done" in server_output + + assert "wrote pkt1" in client_output + assert "wrote pkt2" in client_output + assert "wrote pkt3" in client_output + + +# ---- Test 4: readinto with buffer too small returns negative ---- + +BSIM_PB_OVERFLOW_SERVER_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +svc = _bleio.Service(_bleio.UUID(0xFFE0)) +char = _bleio.Characteristic.add_to_service( + svc, _bleio.UUID(0xFFE1), + properties=_bleio.Characteristic.WRITE, + read_perm=_bleio.Attribute.NO_ACCESS, + write_perm=_bleio.Attribute.OPEN, + max_length=20, fixed_length=False, +) + +pb = _bleio.PacketBuffer(char, buffer_size=4, max_packet_size=20) +print("service created") + +name = b"CPPBOV" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name +adapter.start_advertising(advertisement, connectable=True) + +for _ in range(80): + if adapter.connected: + break + time.sleep(0.1) +print("connected", adapter.connected) + +# Try to read a 5-byte packet into a 3-byte buffer -> ValueError +try: + data = bytearray(3) + n = 0 + deadline = time.monotonic() + 5.0 + while n == 0 and time.monotonic() < deadline: + n = pb.readinto(data) + if n == 0: + time.sleep(0.05) + print("unexpected success", n) +except ValueError as e: + print("valueerror", e) + +time.sleep(0.5) +for _ in range(80): + if not adapter.connected: + break + time.sleep(0.1) +print("done") +""" + +BSIM_PB_OVERFLOW_CLIENT_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +target = None +for entry in adapter.start_scan(timeout=6.0, active=True): + if entry.connectable and b"CPPBOV" in entry.advertisement_bytes: + target = entry.address + break +adapter.stop_scan() + +connection = adapter.connect(target, timeout=5.0) + +services = connection.discover_remote_services([_bleio.UUID(0xFFE0)]) +remote_char = services[0].characteristics[0] + +# Write a 5-byte packet, but server only has a 3-byte read buffer +remote_char.value = bytes([0x01, 0x02, 0x03, 0x04, 0x05]) +print("wrote 5 bytes") + +time.sleep(0.5) +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while connection.connected and time.monotonic() < timeout: + time.sleep(0.1) +print("done") +""" + + +@pytest.mark.duration(25) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PB_OVERFLOW_SERVER_CODE}) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PB_OVERFLOW_CLIENT_CODE}) +def test_bsim_packet_buffer_readinto_overflow(bsim_phy, circuitpython1, circuitpython2): + """readinto returns negative when packet is larger than buffer.""" + server = circuitpython1 + client = circuitpython2 + + client.wait_until_done() + server.wait_until_done() + + server_output = server.serial.all_output + client_output = client.serial.all_output + + assert "service created" in server_output + assert "valueerror" in server_output + + assert "wrote 5 bytes" in client_output + + +# ---- Test 5: write() with header kwarg ---- + +BSIM_PB_HEADER_SERVER_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +svc = _bleio.Service(_bleio.UUID(0xFFE0)) +char = _bleio.Characteristic.add_to_service( + svc, _bleio.UUID(0xFFE1), + properties=_bleio.Characteristic.WRITE | _bleio.Characteristic.NOTIFY, + read_perm=_bleio.Attribute.NO_ACCESS, + write_perm=_bleio.Attribute.OPEN, + max_length=20, fixed_length=False, +) + +pb = _bleio.PacketBuffer(char, buffer_size=4, max_packet_size=20) +print("service created") + +name = b"CPPBHD" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name +adapter.start_advertising(advertisement, connectable=True) + +for _ in range(80): + if adapter.connected: + break + time.sleep(0.1) +print("connected", adapter.connected) + +# Read an incoming packet +data = bytearray(20) +n = 0 +deadline = time.monotonic() + 5.0 +while n == 0 and time.monotonic() < deadline: + n = pb.readinto(data) + if n == 0: + time.sleep(0.05) +print("received", data[:n]) + +# Response: write body with a header — header goes at start of packet +pb.write(bytes([0x42, 0x4F, 0x44, 0x59]), header=bytes([0x48, 0x44])) # "HD" + "BODY" +print("sent with header") + +for _ in range(80): + if not adapter.connected: + break + time.sleep(0.1) +print("done") +""" + +BSIM_PB_HEADER_CLIENT_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +target = None +for entry in adapter.start_scan(timeout=6.0, active=True): + if entry.connectable and b"CPPBHD" in entry.advertisement_bytes: + target = entry.address + break +adapter.stop_scan() + +connection = adapter.connect(target, timeout=5.0) + +services = connection.discover_remote_services([_bleio.UUID(0xFFE0)]) +remote_char = services[0].characteristics[0] + +# Write a packet to trigger the server's response +remote_char.value = bytes([0x47, 0x4F]) +print("wrote trigger") + +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while connection.connected and time.monotonic() < timeout: + time.sleep(0.1) +print("done") +""" + + +@pytest.mark.duration(25) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PB_HEADER_SERVER_CODE}) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PB_HEADER_CLIENT_CODE}) +def test_bsim_packet_buffer_write_header(bsim_phy, circuitpython1, circuitpython2): + """write() header kwarg prepends to packet body.""" + server = circuitpython1 + client = circuitpython2 + + client.wait_until_done() + server.wait_until_done() + + server_output = server.serial.all_output + client_output = client.serial.all_output + + assert "service created" in server_output + assert "received" in server_output + assert "sent with header" in server_output + + assert "wrote trigger" in client_output + + +# ---- Test 6: incoming_packet_length / outgoing_packet_length ---- + +BSIM_PB_LENGTHS_CODE = """\ +import _bleio + +svc = _bleio.Service(_bleio.UUID(0xFFE0)) +char = _bleio.Characteristic.add_to_service( + svc, _bleio.UUID(0xFFE1), + properties=_bleio.Characteristic.WRITE | _bleio.Characteristic.NOTIFY, + read_perm=_bleio.Attribute.NO_ACCESS, + write_perm=_bleio.Attribute.OPEN, + max_length=20, fixed_length=False, +) + +pb = _bleio.PacketBuffer(char, buffer_size=2, max_packet_size=15) + +# incoming_packet_length reflects the max we can receive (characteristic max_length) +print("incoming", pb.incoming_packet_length) +# outgoing_packet_length requires a connection to know the negotiated ATT MTU, +# so without one it raises ValueError (server-side NOTIFY is MTU-bounded). +try: + print("outgoing", pb.outgoing_packet_length) +except ValueError as e: + print("outgoing valueerror", e) +print("done") +""" + + +@pytest.mark.duration(5) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PB_LENGTHS_CODE}) +def test_bsim_packet_buffer_packet_lengths(bsim_phy, circuitpython): + """incoming_packet_length and outgoing_packet_length properties.""" + circuitpython.wait_until_done() + output = circuitpython.serial.all_output + + # Server-side local characteristic: + # incoming = max_length = 20 + # outgoing requires a connection to know the ATT MTU, so it raises without one. + assert "incoming 20" in output + assert "outgoing valueerror" in output + assert "done" in output + + +# ---- Test 7: disconnect / reconnect, conn tracking ---- + +BSIM_PB_RECONNECT_SERVER_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +svc = _bleio.Service(_bleio.UUID(0xFFE0)) +char = _bleio.Characteristic.add_to_service( + svc, _bleio.UUID(0xFFE1), + properties=_bleio.Characteristic.WRITE | _bleio.Characteristic.NOTIFY, + read_perm=_bleio.Attribute.NO_ACCESS, + write_perm=_bleio.Attribute.OPEN, + max_length=20, fixed_length=False, +) + +pb = _bleio.PacketBuffer(char, buffer_size=4, max_packet_size=20) +print("service created") + +name = b"CPPBRC" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name +adapter.start_advertising(advertisement, connectable=True) + +# First connection +for _ in range(80): + if adapter.connected: + break + time.sleep(0.1) +print("connected1", adapter.connected) + +# Read first packet +data = bytearray(20) +n = 0 +deadline = time.monotonic() + 5.0 +while n == 0 and time.monotonic() < deadline: + n = pb.readinto(data) + if n == 0: + time.sleep(0.05) +print("packet1", data[:n]) + +# Send a response to confirm the tracked conn works +pb.write(bytes([0x41, 0x43, 0x4B])) # "ACK" +print("sent ack1") + +# Wait for disconnect +for _ in range(80): + if not adapter.connected: + break + time.sleep(0.1) +print("disconnected") + +# Start advertising again for second connection +adapter.start_advertising(advertisement, connectable=True) + +# Second connection +for _ in range(80): + if adapter.connected: + break + time.sleep(0.1) +print("connected2", adapter.connected) + +# Read second packet (should track new conn) +n2 = 0 +deadline2 = time.monotonic() + 5.0 +while n2 == 0 and time.monotonic() < deadline2: + n2 = pb.readinto(data) + if n2 == 0: + time.sleep(0.05) +print("packet2", data[:n2]) + +pb.write(bytes([0x41, 0x43, 0x4B])) # "ACK" +print("sent ack2") + +for _ in range(80): + if not adapter.connected: + break + time.sleep(0.1) +print("done") +""" + +BSIM_PB_RECONNECT_CLIENT_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +# First connection +target = None +for entry in adapter.start_scan(timeout=6.0, active=True): + if entry.connectable and b"CPPBRC" in entry.advertisement_bytes: + target = entry.address + break +adapter.stop_scan() + +connection = adapter.connect(target, timeout=5.0) +print("connected1", connection.connected) + +services = connection.discover_remote_services([_bleio.UUID(0xFFE0)]) +remote_char = services[0].characteristics[0] + +# Write first packet +remote_char.value = bytes([0x46, 0x49, 0x52, 0x53, 0x54]) # "FIRST" +print("wrote first") + +time.sleep(0.5) + +# Disconnect +connection.disconnect() +timeout = time.monotonic() + 4.0 +while connection.connected and time.monotonic() < timeout: + time.sleep(0.1) +print("disconnected") + +# Wait for server to re-advertise +time.sleep(0.5) + +# Second connection +target = None +for entry in adapter.start_scan(timeout=6.0, active=True): + if entry.connectable and b"CPPBRC" in entry.advertisement_bytes: + target = entry.address + break +adapter.stop_scan() + +connection = adapter.connect(target, timeout=5.0) +print("connected2", connection.connected) + +services = connection.discover_remote_services([_bleio.UUID(0xFFE0)]) +remote_char = services[0].characteristics[0] + +# Write second packet +remote_char.value = bytes([0x53, 0x45, 0x43, 0x4E, 0x44]) # "SECOND" +print("wrote second") + +time.sleep(0.5) +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while connection.connected and time.monotonic() < timeout: + time.sleep(0.1) +print("done") +""" + + +@pytest.mark.duration(30) +@pytest.mark.circuitpy_drive( + {"code.py": BSIM_PB_RECONNECT_SERVER_CODE, "settings.toml": "CIRCUITPY_BLE_WORKFLOW = false\n"} +) +@pytest.mark.circuitpy_drive( + {"code.py": BSIM_PB_RECONNECT_CLIENT_CODE, "settings.toml": "CIRCUITPY_BLE_WORKFLOW = false\n"} +) +def test_bsim_packet_buffer_reconnect(bsim_phy, circuitpython1, circuitpython2): + """PacketBuffer tracks conn through disconnect / reconnect.""" + server = circuitpython1 + client = circuitpython2 + + client.wait_until_done() + server.wait_until_done() + + server_output = server.serial.all_output + client_output = client.serial.all_output + + assert "service created" in server_output + assert "connected1 True" in server_output + assert "packet1" in server_output + assert "sent ack1" in server_output + assert "disconnected" in server_output + assert "connected2 True" in server_output + assert "packet2" in server_output + assert "sent ack2" in server_output + + assert "wrote first" in client_output + assert "wrote second" in client_output + + +# ---- Test 8: outgoing_packet_length bounded by negotiated MTU ---- +# +# Regression test for the "No ATT channel for MTU" bug. The server-side +# PacketBuffer's outgoing_packet_length must be bounded by the negotiated +# ATT MTU (ATT_MTU - 3), not just max_packet_size. Without that bounding, +# the workflow packs a notification larger than the MTU and bt_gatt_notify_cb +# fails silently ("No ATT channel for MTU"), so the client never receives it. +# +# The characteristic is configured with max_length=600 and max_packet_size=600, +# deliberately larger than any plausible negotiated ATT MTU (the stack supports +# up to CONFIG_BT_L2CAP_TX_MTU = 515, payload 512). So without the fix, +# outgoing_packet_length returns 600 and the 600-byte notification always +# exceeds the MTU and is dropped — regardless of whether an MTU exchange +# happened. With the fix, outgoing_packet_length is ATT_MTU - 3 (≤ 512) and the +# notification fits and is delivered. + +BSIM_PB_MTU_SERVER_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +svc = _bleio.Service(_bleio.UUID(0xFFE0)) +char = _bleio.Characteristic.add_to_service( + svc, _bleio.UUID(0xFFE1), + properties=_bleio.Characteristic.WRITE | _bleio.Characteristic.NOTIFY, + read_perm=_bleio.Attribute.NO_ACCESS, + write_perm=_bleio.Attribute.OPEN, + max_length=600, fixed_length=False, +) + +# max_packet_size deliberately larger than any negotiated ATT MTU so that an +# unbounded outgoing_packet_length always produces an oversized, undeliverable +# notification regardless of MTU-exchange timing. +pb = _bleio.PacketBuffer(char, buffer_size=4, max_packet_size=600) +print("service created") + +name = b"CPPBMT" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name +adapter.start_advertising(advertisement, connectable=True) +print("advertising") + +for _ in range(80): + if adapter.connected: + break + time.sleep(0.1) +print("connected", adapter.connected) + +# Wait for the client to subscribe (CCCD write) and send a trigger write. +data = bytearray(600) +n = 0 +deadline = time.monotonic() + 5.0 +while n == 0 and time.monotonic() < deadline: + n = pb.readinto(data) + if n == 0: + time.sleep(0.05) +print("trigger", data[:n]) + +# The negotiated ATT MTU determines the largest notification payload. The +# connection's max_packet_length is ATT_MTU - 3. +conn = adapter.connections[0] +mtu_payload = conn.max_packet_length +print("mtu_payload", mtu_payload) + +# outgoing_packet_length must be bounded by the MTU, not max_packet_size. +opl = pb.outgoing_packet_length +print("outgoing", opl) + +# Send a response sized exactly to outgoing_packet_length, like the BLE +# file-transfer workflow does. With the fix this fits the MTU and is delivered. +response = bytes([0xAA]) * opl +pb.write(response) +print("sent", len(response)) + +for _ in range(80): + if not adapter.connected: + break + time.sleep(0.1) +print("done") +""" + +BSIM_PB_MTU_CLIENT_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +target = None +for entry in adapter.start_scan(timeout=6.0, active=True): + if entry.connectable and b"CPPBMT" in entry.advertisement_bytes: + target = entry.address + print("found server") + break +adapter.stop_scan() + +connection = adapter.connect(target, timeout=5.0) +print("connected", connection.connected) + +services = connection.discover_remote_services([_bleio.UUID(0xFFE0)]) +remote_char = services[0].characteristics[0] +print("found char, props", remote_char.properties) + +# Creating a client-side PacketBuffer on a NOTIFY characteristic writes the +# CCCD to subscribe, which gives the server a connection to notify. +client_pb = _bleio.PacketBuffer(remote_char, buffer_size=4, max_packet_size=600) +print("subscribed") + +# Trigger the server's response with a direct write. +remote_char.value = b"GO" +print("wrote trigger") + +# Read the notification response. Without the MTU fix the server sends an +# oversized notification that the ATT layer drops, so this times out. +buf = bytearray(600) +n = 0 +deadline = time.monotonic() + 5.0 +while n == 0 and time.monotonic() < deadline: + n = client_pb.readinto(buf) + if n == 0: + time.sleep(0.05) +if n > 0: + print("received_ok", n, bytes(buf[:n])) +else: + print("received_none") + +time.sleep(0.5) +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while connection.connected and time.monotonic() < timeout: + time.sleep(0.1) +print("done") +""" + + +@pytest.mark.duration(30) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PB_MTU_SERVER_CODE}) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PB_MTU_CLIENT_CODE}) +def test_bsim_packet_buffer_outgoing_mtu_bounded(bsim_phy, circuitpython1, circuitpython2): + """Server-side outgoing_packet_length is bounded by the negotiated ATT MTU. + + Without the fix, outgoing_packet_length returns max_packet_size (200) and + the resulting notification exceeds the bsim default ATT MTU (23), so the + ATT layer drops it ("No ATT channel for MTU") and the client receives + nothing. With the fix, outgoing_packet_length is ATT_MTU - 3 (20) and the + notification is delivered to the client. + """ + server = circuitpython1 + client = circuitpython2 + + client.wait_until_done() + server.wait_until_done() + + server_output = server.serial.all_output + client_output = client.serial.all_output + + assert "service created" in server_output + assert "connected True" in server_output + assert "trigger" in server_output + + # outgoing_packet_length must not exceed the connection's MTU-derived + # max_packet_length. Without the fix this is 200 vs 20. + mtu_match = re.search(r"mtu_payload (\d+)", server_output) + outgoing_match = re.search(r"outgoing (\d+)", server_output) + assert mtu_match is not None, f"mtu_payload not printed: {server_output}" + assert outgoing_match is not None, f"outgoing not printed: {server_output}" + mtu_payload = int(mtu_match.group(1)) + outgoing = int(outgoing_match.group(1)) + assert outgoing <= mtu_payload, ( + f"outgoing_packet_length {outgoing} exceeds MTU payload {mtu_payload}; " + "notifications this size would be dropped by the ATT layer" + ) + assert outgoing < 600, f"outgoing_packet_length {outgoing} not bounded below max_packet_size" + + # The notification sized to outgoing_packet_length must actually arrive. + assert "received_ok" in client_output, ( + f"client never received the notification (MTU-bound bug): {client_output}" + ) + assert "received_none" not in client_output + + received_match = re.search(r"received_ok (\d+)", client_output) + assert received_match is not None + received = int(received_match.group(1)) + assert received == outgoing, f"client received {received} bytes, server sent {outgoing}" + + +# ---- Test: client-side PacketBuffer write (remote WRITE_NO_RESPONSE) ---- +# +# Covers the client-side write path in common_hal_bleio_packet_buffer_write +# (the is_remote branch): a CP central wraps a remote characteristic in a +# _bleio.PacketBuffer and writes to it via pb.write(...), which must reach the +# peripheral's server-side PacketBuffer. This mirrors how the BLE file-transfer +# library sends commands (raw.write). It also exercises the header+data combine +# in the client write path via pb.write(data, header=...). +# +# The characteristic uses WRITE_NO_RESPONSE | NOTIFY with open permissions, so +# no pairing is required and the test runs on both bsim boards. + +BSIM_PB_CLIENT_WRITE_SERVER_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +svc = _bleio.Service(_bleio.UUID(0xFFE0)) +char = _bleio.Characteristic.add_to_service( + svc, _bleio.UUID(0xFFE1), + properties=_bleio.Characteristic.WRITE_NO_RESPONSE | _bleio.Characteristic.NOTIFY, + read_perm=_bleio.Attribute.OPEN, + write_perm=_bleio.Attribute.OPEN, + max_length=20, fixed_length=False, +) + +# Server-side PacketBuffer: reads incoming WRITE_NO_RESPONSE, echoes via NOTIFY. +pb = _bleio.PacketBuffer(char, buffer_size=4, max_packet_size=20) +print("service created") + +name = b"CPPBCW" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name +adapter.start_advertising(advertisement, connectable=True) +print("advertising") + +for _ in range(80): + if adapter.connected: + break + time.sleep(0.1) +print("connected", adapter.connected) + +data = bytearray(20) +for _ in range(2): + n = 0 + deadline = time.monotonic() + 5.0 + while n == 0 and time.monotonic() < deadline: + n = pb.readinto(data) + if n == 0: + time.sleep(0.05) + print("received", bytes(data[:n])) + # Echo the packet straight back over NOTIFY. + pb.write(bytes(data[:n])) + print("echoed", bytes(data[:n])) + +for _ in range(80): + if not adapter.connected: + break + time.sleep(0.1) +print("done") +""" + +BSIM_PB_CLIENT_WRITE_CLIENT_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +target = None +for entry in adapter.start_scan(timeout=6.0, active=True): + if entry.connectable and b"CPPBCW" in entry.advertisement_bytes: + target = entry.address + print("found server") + break +adapter.stop_scan() + +connection = adapter.connect(target, timeout=5.0) +print("connected", connection.connected) + +services = connection.discover_remote_services([_bleio.UUID(0xFFE0)]) +print("discovered services", len(services)) + +remote_char = services[0].characteristics[0] +print("found char, props", remote_char.properties) + +# Client-side PacketBuffer on the remote characteristic: subscribes to NOTIFY +# (readinto) and writes to the remote (pb.write -> client-side write path). +cpb = _bleio.PacketBuffer(remote_char, buffer_size=4, max_packet_size=20) + +# Write 1: plain write (no header) — exercises bt_gatt_write_without_response. +cpb.write(b"PING") +print("wrote ping") + +# Write 2: write with a header — exercises the header+data combine in the +# client write path. "B" + "ODY" is sent as a single "BODY" packet. +cpb.write(b"ODY", header=b"B") +print("wrote body with header") + +# Read both NOTIFY echoes back. +buf = bytearray(20) +for _ in range(2): + n = 0 + deadline = time.monotonic() + 5.0 + while n == 0 and time.monotonic() < deadline: + n = cpb.readinto(buf) + if n == 0: + time.sleep(0.05) + print("client received", bytes(buf[:n])) + +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while connection.connected and time.monotonic() < timeout: + time.sleep(0.1) +print("done") +""" + + +@pytest.mark.duration(35) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PB_CLIENT_WRITE_SERVER_CODE}) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PB_CLIENT_WRITE_CLIENT_CODE}) +def test_bsim_packet_buffer_client_write(bsim_phy, circuitpython1, circuitpython2): + """A CP central writes to a remote characteristic via a client-side + PacketBuffer (pb.write), and the peripheral echoes it back over NOTIFY. + + Covers the client-side write path (remote WRITE_NO_RESPONSE), including the + header+data combine, mirroring how the BLE file-transfer library sends + commands. + """ + server = circuitpython1 + client = circuitpython2 + + client.wait_until_done() + server.wait_until_done() + + server_output = server.serial.all_output + client_output = client.serial.all_output + + # Server received both client writes (plain + header-combined). + assert "received b'PING'" in server_output, ( + f"server never received the plain client write: {server_output}" + ) + assert "received b'BODY'" in server_output, ( + f"server never received the header-combined client write: {server_output}" + ) + assert "echoed b'PING'" in server_output + assert "echoed b'BODY'" in server_output + assert "done" in server_output + + # Client wrote both and got both echoes back over NOTIFY. + assert "wrote ping" in client_output, f"client plain write did not complete: {client_output}" + assert "wrote body with header" in client_output, ( + f"client header write did not complete: {client_output}" + ) + # Be vague here because PING and BODY may be in separate packets or merged. + assert "b'PING" in client_output, f"client never received the PING echo: {client_output}" + assert "BODY'" in client_output, f"client never received the BODY echo: {client_output}" + assert "done" in client_output diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_pairing.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_pairing.py new file mode 100644 index 00000000000..850b2fc8957 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_pairing.py @@ -0,0 +1,364 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""BLE pairing tests for the bsim boards.""" + +import pytest + +BSIM_PERIPHERAL_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +name = b"PAIRPERIPH" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name + +print("peripheral start") +adapter.start_advertising(advertisement, connectable=True) +print("peripheral advertising", adapter.advertising) + +# Wait for connection +timeout = time.monotonic() + 12.0 +while not adapter.connected and time.monotonic() < timeout: + time.sleep(0.01) + +if not adapter.connected: + print("peripheral connect timed out") + raise SystemExit(1) + +print("peripheral connected", adapter.connected) + +# Wait for pairing to complete (central initiates it) +# The peripheral's SMP module responds automatically. +timeout = time.monotonic() + 10.0 +paired = False +while time.monotonic() < timeout: + if adapter.connected and len(adapter.connections) > 0: + conn = adapter.connections[0] + if conn.paired: + paired = True + break + time.sleep(0.1) + +print("peripheral paired", paired) + +# Wait for disconnect +timeout = time.monotonic() + 10.0 +while adapter.connected and time.monotonic() < timeout: + time.sleep(0.1) + +print("peripheral disconnected", adapter.connected, len(adapter.connections)) +""" + +BSIM_CENTRAL_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +print("central start") +target = None +for entry in adapter.start_scan(timeout=8.0, active=True): + if entry.connectable and b"PAIRPERIPH" in entry.advertisement_bytes: + target = entry.address + print("central found target") + break +adapter.stop_scan() +print("central have target", target is not None) + +if target is None: + raise RuntimeError("No connectable target found") + +connection = adapter.connect(target, timeout=5.0) +print("central connected", connection.connected, adapter.connected, len(adapter.connections)) + +# Pair with the peripheral (Just Works pairing) +connection.pair() +print("central paired", connection.paired) + +# Disconnect cleanly +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while (connection.connected or adapter.connected) and time.monotonic() < timeout: + time.sleep(0.1) + +print("central disconnected", connection.connected, adapter.connected, len(adapter.connections)) +""" + + +@pytest.mark.duration(22) +@pytest.mark.circuitpy_drive( + {"code.py": BSIM_PERIPHERAL_CODE, "settings.toml": "CIRCUITPY_BLE_WORKFLOW = false\n"} +) +@pytest.mark.circuitpy_drive( + {"code.py": BSIM_CENTRAL_CODE, "settings.toml": "CIRCUITPY_BLE_WORKFLOW = false\n"} +) +def test_bsim_pairing_cp_to_cp(bsim_phy, board, circuitpython1, circuitpython2): + """Two CP instances: device 0 peripheral, device 1 central pairs to it.""" + peripheral = circuitpython1 + central = circuitpython2 + + central.wait_until_done() + peripheral.wait_until_done() + + periph_output = peripheral.serial.all_output + central_output = central.serial.all_output + + # Peripheral assertions + assert "peripheral start" in periph_output + assert "peripheral advertising True" in periph_output + assert "peripheral connected True" in periph_output + assert "peripheral paired True" in periph_output + assert "peripheral disconnected False 0" in periph_output + + # Central assertions + assert "central start" in central_output + assert "central found target" in central_output + assert "central have target True" in central_output + assert "central connected True True 1" in central_output + assert "central paired True" in central_output + assert "central disconnected False False 0" in central_output + + +BSIM_CENTRAL_PAIRED_PROPERTY_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +print("central start") +target = None +for entry in adapter.start_scan(timeout=8.0, active=True): + if entry.connectable and b"PAIRPERIPH" in entry.advertisement_bytes: + target = entry.address + print("central found target") + break +adapter.stop_scan() + +if target is None: + raise RuntimeError("No connectable target found") + +connection = adapter.connect(target, timeout=5.0) +print("central connected", connection.connected) + +# Check paired is False before pairing +print("central paired before", connection.paired) + +connection.pair() +print("central paired after", connection.paired) + +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while (connection.connected or adapter.connected) and time.monotonic() < timeout: + time.sleep(0.1) + +print("central disconnected", connection.connected) +""" + + +@pytest.mark.duration(22) +@pytest.mark.circuitpy_drive( + {"code.py": BSIM_PERIPHERAL_CODE, "settings.toml": "CIRCUITPY_BLE_WORKFLOW = false\n"} +) +@pytest.mark.circuitpy_drive( + { + "code.py": BSIM_CENTRAL_PAIRED_PROPERTY_CODE, + "settings.toml": "CIRCUITPY_BLE_WORKFLOW = false\n", + } +) +def test_bsim_pairing_paired_property(bsim_phy, board, circuitpython1, circuitpython2): + """Verify connection.paired transitions from False to True after pairing.""" + peripheral = circuitpython1 + central = circuitpython2 + + central.wait_until_done() + peripheral.wait_until_done() + + periph_output = peripheral.serial.all_output + central_output = central.serial.all_output + + assert "peripheral paired True" in periph_output + + assert "central paired before False" in central_output + assert "central paired after True" in central_output + assert "central disconnected False" in central_output + + +BSIM_PERIPHERAL_BOND_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +name = b"BONDPERIPH" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name + +print("peripheral start") + +# First connection - pair +adapter.start_advertising(advertisement, connectable=True) +print("peripheral advertising", adapter.advertising) + +timeout = time.monotonic() + 12.0 +while not adapter.connected and time.monotonic() < timeout: + time.sleep(0.01) + +if not adapter.connected: + print("peripheral connect1 timed out") + raise SystemExit(1) + +print("peripheral connected1", adapter.connected) + +# Wait for pairing +timeout = time.monotonic() + 10.0 +paired = False +while time.monotonic() < timeout: + if adapter.connected and len(adapter.connections) > 0: + conn = adapter.connections[0] + if conn.paired: + paired = True + break + time.sleep(0.1) + +print("peripheral paired1", paired) + +# Wait for disconnect +timeout = time.monotonic() + 10.0 +while adapter.connected and time.monotonic() < timeout: + time.sleep(0.1) + +print("peripheral disconnected1", adapter.connected, len(adapter.connections)) + +# Second connection - bond should persist through storage. +# Calling pair() uses the stored bond keys without re-pairing. +time.sleep(0.5) +adapter.start_advertising(advertisement, connectable=True) +print("peripheral advertising2", adapter.advertising) + +timeout = time.monotonic() + 12.0 +while not adapter.connected and time.monotonic() < timeout: + time.sleep(0.01) + +if not adapter.connected: + print("peripheral connect2 timed out") + raise SystemExit(1) + +print("peripheral connected2", adapter.connected) + +# Request security to restore bond encryption +conn = adapter.connections[0] +conn.pair() +print("peripheral paired2", conn.paired) + +# Wait for disconnect +timeout = time.monotonic() + 10.0 +while adapter.connected and time.monotonic() < timeout: + time.sleep(0.1) + +print("peripheral disconnected2", adapter.connected) +""" + +BSIM_CENTRAL_BOND_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +print("central start") +target_addr = None +for entry in adapter.start_scan(timeout=8.0, active=True): + if entry.connectable and b"BONDPERIPH" in entry.advertisement_bytes: + target_addr = entry.address + print("central found target") + break +adapter.stop_scan() +print("central have target", target_addr is not None) + +if target_addr is None: + raise RuntimeError("No connectable target found") + +# First connection - pair +connection = adapter.connect(target_addr, timeout=5.0) +print("central connected1", connection.connected, adapter.connected, len(adapter.connections)) + +print("central paired before1", connection.paired) +connection.pair() +print("central paired after1", connection.paired) + +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while (connection.connected or adapter.connected) and time.monotonic() < timeout: + time.sleep(0.1) + +print("central disconnected1", connection.connected, adapter.connected) + +# Second connection - bond should be restored from storage. +# Calling pair() uses the stored bond keys without re-pairing. +time.sleep(1.0) +connection2 = adapter.connect(target_addr, timeout=5.0) +print("central connected2", connection2.connected, adapter.connected, len(adapter.connections)) + +connection2.pair() +print("central paired bond", connection2.paired) + +connection2.disconnect() + +timeout = time.monotonic() + 4.0 +while (connection2.connected or adapter.connected) and time.monotonic() < timeout: + time.sleep(0.1) + +print("central disconnected2", connection2.connected, adapter.connected) +""" + + +@pytest.mark.duration(35) +@pytest.mark.circuitpy_drive( + {"code.py": BSIM_PERIPHERAL_BOND_CODE, "settings.toml": "CIRCUITPY_BLE_WORKFLOW = false\n"} +) +@pytest.mark.circuitpy_drive( + {"code.py": BSIM_CENTRAL_BOND_CODE, "settings.toml": "CIRCUITPY_BLE_WORKFLOW = false\n"} +) +def test_bsim_bonding_persistence(bsim_phy, board, circuitpython1, circuitpython2): + """Verify bonds are saved to storage and survive disconnect/reconnect. + + Central pairs with peripheral, disconnects, then reconnects. + On the second connection the bond should be restored from storage + and connection.paired should be True without calling pair() again. + """ + peripheral = circuitpython1 + central = circuitpython2 + + central.wait_until_done() + peripheral.wait_until_done() + + periph_output = peripheral.serial.all_output + central_output = central.serial.all_output + + # Peripheral assertions - first connection + assert "peripheral start" in periph_output + assert "peripheral advertising True" in periph_output + assert "peripheral connected1 True" in periph_output + assert "peripheral paired1 True" in periph_output + assert "peripheral disconnected1 False 0" in periph_output + # Peripheral - second connection (private advertising, bond restored from storage) + assert "peripheral advertising2 True" in periph_output + assert "peripheral connected2 True" in periph_output + assert "peripheral paired2 True" in periph_output + assert "peripheral disconnected2 False" in periph_output + + # Central assertions - first connection + assert "central start" in central_output + assert "central found target" in central_output + assert "central have target True" in central_output + assert "central connected1 True True 1" in central_output + assert "central paired before1 False" in central_output + assert "central paired after1 True" in central_output + assert "central disconnected1 False False" in central_output + # Central - second connection (bond restored from storage) + assert "central connected2 True True 1" in central_output + assert "central paired bond True" in central_output + assert "central disconnected2 False False" in central_output diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_peripheral.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_peripheral.py index 7a4bbfaecd9..ff99b789f0c 100644 --- a/ports/zephyr-cp/tests/bsim/test_bsim_ble_peripheral.py +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_peripheral.py @@ -1,11 +1,10 @@ # SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries # SPDX-License-Identifier: MIT -"""BLE peripheral connection tests for nrf5340bsim.""" +"""BLE peripheral connection tests for bsim.""" import pytest -pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") BSIM_PERIPHERAL_CODE = """\ import _bleio @@ -74,7 +73,9 @@ @pytest.mark.zephyr_sample("bluetooth/central") @pytest.mark.duration(14) -@pytest.mark.circuitpy_drive({"code.py": BSIM_PERIPHERAL_CODE}) +@pytest.mark.circuitpy_drive( + {"code.py": BSIM_PERIPHERAL_CODE, "settings.toml": "CIRCUITPY_BLE_WORKFLOW = false\n"} +) def test_bsim_peripheral_zephyr_central(bsim_phy, circuitpython, zephyr_sample): """Advertise as connectable from CP; Zephyr central connects and disconnects.""" central = zephyr_sample @@ -94,7 +95,7 @@ def test_bsim_peripheral_zephyr_central(bsim_phy, circuitpython, zephyr_sample): assert "Disconnected:" in central_output -@pytest.mark.duration(14) +@pytest.mark.duration(20) @pytest.mark.circuitpy_drive({"code.py": BSIM_PERIPHERAL_CODE}) @pytest.mark.circuitpy_drive({"code.py": BSIM_CENTRAL_CODE}) def test_bsim_peripheral_cp_central(bsim_phy, circuitpython1, circuitpython2): diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py index 19455b7bfa3..9f58914ade4 100644 --- a/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py @@ -1,11 +1,10 @@ # SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries # SPDX-License-Identifier: MIT -"""BLE scanning tests for nrf5340bsim.""" +"""BLE scanning tests for bsim.""" import pytest -pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") BSIM_SCAN_CODE = """\ import _bleio @@ -56,6 +55,45 @@ print("scan run done", found) """ +BSIM_SCAN_ENTRY_PROPS_CODE = """\ +import _bleio + +adapter = _bleio.adapter +print("scan start") +found = None +for entry in adapter.start_scan(timeout=4.0, active=True): + if b"zephyrproject" in entry.advertisement_bytes: + found = entry + break +adapter.stop_scan() + +if found is not None: + print("rssi", found.rssi <= 0 and found.rssi > -100) + print("connectable", found.connectable) + print("scan_response", found.scan_response) + print("address_bytes", len(found.address.address_bytes)) + print("address_type", found.address.type in (0, 1, 2, 3)) + print("adv_bytes_type", isinstance(found.advertisement_bytes, bytes)) +else: + print("no entry found") +print("done") +""" + +BSIM_SCAN_PASSIVE_CODE = """\ +import _bleio + +adapter = _bleio.adapter +print("scan passive start") +found = False +for entry in adapter.start_scan(timeout=4.0, active=False): + if b"zephyrproject" in entry.advertisement_bytes: + print("found beacon passive") + found = True + break +adapter.stop_scan() +print("scan passive done", found) +""" + @pytest.mark.zephyr_sample("bluetooth/beacon") @pytest.mark.circuitpy_drive({"code.py": BSIM_SCAN_CODE}) @@ -73,7 +111,7 @@ def test_bsim_scan_zephyr_beacon(bsim_phy, circuitpython, zephyr_sample): @pytest.mark.zephyr_sample("bluetooth/beacon") @pytest.mark.code_py_runs(2) -@pytest.mark.duration(4) +@pytest.mark.duration(20) @pytest.mark.circuitpy_drive({"code.py": BSIM_SCAN_RELOAD_CODE}) def test_bsim_scan_zephyr_beacon_reload(bsim_phy, circuitpython, zephyr_sample): """Scan for Zephyr beacon, soft reload, and scan again.""" @@ -91,22 +129,33 @@ def test_bsim_scan_zephyr_beacon_reload(bsim_phy, circuitpython, zephyr_sample): assert output.count("scan run done True") >= 2 -@pytest.mark.xfail(strict=False, reason="scan without stop_scan may fail on reload") @pytest.mark.zephyr_sample("bluetooth/beacon") -@pytest.mark.code_py_runs(2) -@pytest.mark.duration(8) -@pytest.mark.circuitpy_drive({"code.py": BSIM_SCAN_RELOAD_NO_STOP_CODE}) -def test_bsim_scan_zephyr_beacon_reload_no_stop(bsim_phy, circuitpython, zephyr_sample): - """Scan for Zephyr beacon without explicit stop, soft reload, and scan again.""" +@pytest.mark.circuitpy_drive({"code.py": BSIM_SCAN_ENTRY_PROPS_CODE}) +def test_bsim_scan_entry_properties(bsim_phy, circuitpython, zephyr_sample): + """Verify ScanEntry properties: rssi, connectable, scan_response, address, advertisement_bytes.""" _ = zephyr_sample - circuitpython.serial.wait_for("scan run done") - circuitpython.serial.wait_for("Press any key to enter the REPL") - circuitpython.serial.write("\x04") + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "rssi True" in output + assert "connectable False" in output # beacon is non-connectable + assert "scan_response False" in output + assert "address_bytes 6" in output + assert "address_type True" in output + assert "adv_bytes_type True" in output + assert "done" in output + + +@pytest.mark.zephyr_sample("bluetooth/beacon") +@pytest.mark.circuitpy_drive({"code.py": BSIM_SCAN_PASSIVE_CODE}) +def test_bsim_scan_passive(bsim_phy, circuitpython, zephyr_sample): + """Passive scan finds Zephyr beacon.""" + _ = zephyr_sample circuitpython.wait_until_done() output = circuitpython.serial.all_output - assert output.count("scan run start") >= 2 - assert output.count("found beacon run") >= 2 - assert output.count("scan run done True") >= 2 + assert "scan passive start" in output + assert "found beacon passive" in output + assert "scan passive done True" in output diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_service.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_service.py new file mode 100644 index 00000000000..1d4174835d0 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_service.py @@ -0,0 +1,715 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""BLE GATT service tests for bsim.""" + +import pytest + +from .conftest import get_library_files + +_ADAFRUIT_BLE = get_library_files("adafruit_ble") + +# --------------------------------------------------------------------------- +# Shared service libraries +# --------------------------------------------------------------------------- + +BATTERY_LIB = """ +from adafruit_ble.services import Service +from adafruit_ble.characteristics import Characteristic, Attribute +from adafruit_ble.uuid import StandardUUID + + +class BatteryService(Service): + uuid = StandardUUID(0x180F) + level = Characteristic( + uuid=StandardUUID(0x2A19), + properties=Characteristic.READ | Characteristic.WRITE, + read_perm=Attribute.OPEN, + write_perm=Attribute.OPEN, + max_length=1, + fixed_length=True, + initial_value=bytes([75]), + )""" + +HEART_RATE_LIB = """ +from adafruit_ble.services import Service +from adafruit_ble.characteristics import Characteristic, Attribute +from adafruit_ble.uuid import StandardUUID + + +class HeartRateService(Service): + uuid = StandardUUID(0x180D) + measurement = Characteristic( + uuid=StandardUUID(0x2A37), + properties=Characteristic.READ, + read_perm=Attribute.OPEN, + write_perm=Attribute.NO_ACCESS, + max_length=2, + fixed_length=True, + initial_value=bytes([0, 72]), + )""" + +# =================================================================== +# Test 1: Battery Service (Zephyr central) +# =================================================================== + +BSIM_SERVICE_CODE = """\ +import time +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from battery_service import BatteryService + +ble = BLERadio() +svc = BatteryService() +print("service created") + +advertisement = ProvideServicesAdvertisement(svc) +ble.start_advertising(advertisement) +print("advertising") + +for _ in range(80): + if ble.connected: + break + time.sleep(0.1) +print("connected", ble.connected) + +for _ in range(80): + if not ble.connected: + break + time.sleep(0.1) +print("done") +""" + + +@pytest.mark.zephyr_sample("tests/bsim/samples/central_battery_client") +@pytest.mark.duration(14) +@pytest.mark.circuitpy_drive( + { + "code.py": BSIM_SERVICE_CODE, + "settings.toml": "CIRCUITPY_BLE_WORKFLOW = false\n", + "battery_service.py": BATTERY_LIB, + **_ADAFRUIT_BLE, + } +) +def test_bsim_service_battery(bsim_phy, circuitpython, zephyr_sample): + """CP hosts BatteryService; Zephyr central reads battery level.""" + circuitpython.wait_until_done() + + cp_output = circuitpython.serial.all_output + sample_output = zephyr_sample.serial.all_output + + assert "service created" in cp_output + assert "connected True" in cp_output + assert "Battery Level: 75" in sample_output + + +# =================================================================== +# Test 2: CP client reads/writes CP peripheral +# =================================================================== + +BSIM_SERVER_CODE = """\ +import time +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from battery_service import BatteryService + +ble = BLERadio() +svc = BatteryService() +print("service created") + +advertisement = ProvideServicesAdvertisement(svc) +ble.start_advertising(advertisement) +print("advertising") + +for _ in range(80): + if ble.connected: + break + time.sleep(0.1) +print("connected", ble.connected) + +for _ in range(80): + if not ble.connected: + break + time.sleep(0.1) + +print("final value", list(svc.level)) +print("done") +""" + +BSIM_CLIENT_CODE = """\ +import time +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from battery_service import BatteryService + +ble = BLERadio() + +print("client start") +target = None +for adv in ble.start_scan(ProvideServicesAdvertisement, timeout=6.0): + if adv.connectable: + target = adv + print("found server") + break +ble.stop_scan() +print("have target", target is not None) + +if target is None: + raise RuntimeError("No server found") + +connection = ble.connect(target, timeout=5.0) +print("connected", connection.connected) + +svc = connection[BatteryService] +print("discovered services", 1) +print("discovered chars", 1) # BatteryService has one characteristic + +print("battery level", list(svc.level)) + +# Write a new value +svc.level = bytes([42]) +print("wrote new value") + +time.sleep(0.5) + +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while connection.connected and time.monotonic() < timeout: + time.sleep(0.1) + +print("disconnected", not connection.connected) +""" + + +@pytest.mark.duration(30) +@pytest.mark.circuitpy_drive( + {"code.py": BSIM_SERVER_CODE, "battery_service.py": BATTERY_LIB, **_ADAFRUIT_BLE} +) +@pytest.mark.circuitpy_drive( + {"code.py": BSIM_CLIENT_CODE, "battery_service.py": BATTERY_LIB, **_ADAFRUIT_BLE} +) +def test_bsim_service_cp_client(bsim_phy, circuitpython1, circuitpython2): + """CP peripheral hosts BatteryService; CP central discovers, reads, and writes.""" + server = circuitpython1 + client = circuitpython2 + + client.wait_until_done() + server.wait_until_done() + + server_output = server.serial.all_output + client_output = client.serial.all_output + + assert "service created" in server_output + assert "connected True" in server_output + assert "final value [42]" in server_output + + assert "client start" in client_output + assert "found server" in client_output + assert "discovered services 1" in client_output + assert "discovered chars 1" in client_output + assert "battery level [75]" in client_output + assert "wrote new value" in client_output + assert "disconnected True" in client_output + + +# =================================================================== +# Test 3: Discover all services (no whitelist) +# =================================================================== + +BSIM_DISCOVER_ALL_SERVER_CODE = """\ +import time +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from battery_service import BatteryService +from heart_rate import HeartRateService + +ble = BLERadio() +bas = BatteryService() +hrs = HeartRateService() +print("services created") + +advertisement = ProvideServicesAdvertisement(bas, hrs) +ble.start_advertising(advertisement) + +for _ in range(80): + if ble.connected: + break + time.sleep(0.1) +print("connected", ble.connected) + +for _ in range(80): + if not ble.connected: + break + time.sleep(0.1) +print("done") +""" + +BSIM_DISCOVER_ALL_CLIENT_CODE = """\ +import time +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from battery_service import BatteryService +from heart_rate import HeartRateService + +ble = BLERadio() + +print("client start") +target = None +for adv in ble.start_scan(ProvideServicesAdvertisement, timeout=8.0): + if adv.connectable: + target = adv + print("found server") + break +ble.stop_scan() +print("have target", target is not None) + +if target is None: + raise RuntimeError("No server found") + +connection = ble.connect(target, timeout=5.0) +print("connected", connection.connected) + +# Discover ALL services (no whitelist) — use _bleio directly. +all_services = connection._bleio_connection.discover_remote_services() +print("total services", len(all_services)) + +# Filter to our two known UUIDs (ignore GATT/GAP services the stack may expose) +user_svcs = [s for s in all_services if s.uuid.uuid16 in (0x180F, 0x180D)] +print("user services", len(user_svcs)) + +uuids = sorted([s.uuid.uuid16 for s in user_svcs]) +print("service uuids", uuids) + +# Now read characteristics via adafruit_ble Service bindings. +if BatteryService in connection: + bas = connection[BatteryService] + print("char", hex(0x2a19), list(bas.level)) + +if HeartRateService in connection: + hrs = connection[HeartRateService] + print("char", hex(0x2a37), list(hrs.measurement)) + +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while connection.connected and time.monotonic() < timeout: + time.sleep(0.1) +print("done") +""" + + +@pytest.mark.duration(35) +@pytest.mark.circuitpy_drive( + { + "code.py": BSIM_DISCOVER_ALL_SERVER_CODE, + "battery_service.py": BATTERY_LIB, + "heart_rate.py": HEART_RATE_LIB, + **_ADAFRUIT_BLE, + } +) +@pytest.mark.circuitpy_drive( + { + "code.py": BSIM_DISCOVER_ALL_CLIENT_CODE, + "battery_service.py": BATTERY_LIB, + "heart_rate.py": HEART_RATE_LIB, + **_ADAFRUIT_BLE, + } +) +def test_bsim_service_discover_all(bsim_phy, circuitpython1, circuitpython2): + """Discover all services without a UUID whitelist, verify two user services found.""" + server = circuitpython1 + client = circuitpython2 + + client.wait_until_done() + server.wait_until_done() + + client_output = client.serial.all_output + + assert "user services 2" in client_output + assert "service uuids [6157, 6159]" in client_output # 0x180D=6157, 0x180F=6159 + assert "char 0x2a37 [0, 72]" in client_output + assert "char 0x2a19 [75]" in client_output + + +# =================================================================== +# Test 4: Write-no-response (uses MultiService from multi_service module) +# =================================================================== + +MULTI_LIB = """ +from adafruit_ble.services import Service +from adafruit_ble.characteristics import Characteristic, Attribute +from adafruit_ble.uuid import StandardUUID + + +class MultiService(Service): + uuid = StandardUUID(0x180F) + char_a = Characteristic( + uuid=StandardUUID(0x2A19), + properties=Characteristic.READ, + read_perm=Attribute.OPEN, + write_perm=Attribute.NO_ACCESS, + max_length=1, + fixed_length=True, + initial_value=bytes([10]), + ) + char_b = Characteristic( + uuid=StandardUUID(0x2A1A), + properties=Characteristic.READ | Characteristic.WRITE_NO_RESPONSE, + read_perm=Attribute.OPEN, + write_perm=Attribute.OPEN, + max_length=1, + fixed_length=True, + initial_value=bytes([20]), + ) + char_c = Characteristic( + uuid=StandardUUID(0x2A1B), + properties=Characteristic.READ, + read_perm=Attribute.OPEN, + write_perm=Attribute.NO_ACCESS, + max_length=1, + fixed_length=True, + initial_value=bytes([30]), + )""" + + +BSIM_MULTI_CHAR_SERVER_CODE = """\ +import time +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from multi_service import MultiService + +ble = BLERadio() +svc = MultiService() +# Force binding +svc.char_a +svc.char_b +svc.char_c +print("service created") + +advertisement = ProvideServicesAdvertisement(svc) +ble.start_advertising(advertisement) + +for _ in range(80): + if ble.connected: + break + time.sleep(0.1) +print("connected", ble.connected) + +for _ in range(80): + if not ble.connected: + break + time.sleep(0.1) + +print("char_b final", list(svc.char_b)) +print("done") +""" + +BSIM_WRITE_NR_CLIENT_CODE = """\ +import time +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from multi_service import MultiService + +ble = BLERadio() + +target = None +for adv in ble.start_scan(ProvideServicesAdvertisement, timeout=6.0): + if adv.connectable: + target = adv + break +ble.stop_scan() + +if target is None: + raise RuntimeError("No server found") + +connection = ble.connect(target, timeout=5.0) + +svc = connection[MultiService] + +print("initial", list(svc.char_b)) + +# Write-no-response +svc.char_b = bytes([99]) +print("wrote wnr") + +# Give the server time to process the write +time.sleep(0.5) + +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while connection.connected and time.monotonic() < timeout: + time.sleep(0.1) +print("done") +""" + + +@pytest.mark.duration(25) +@pytest.mark.circuitpy_drive( + {"code.py": BSIM_MULTI_CHAR_SERVER_CODE, "multi_service.py": MULTI_LIB, **_ADAFRUIT_BLE} +) +@pytest.mark.circuitpy_drive( + {"code.py": BSIM_WRITE_NR_CLIENT_CODE, "multi_service.py": MULTI_LIB, **_ADAFRUIT_BLE} +) +def test_bsim_service_write_no_response(bsim_phy, circuitpython1, circuitpython2): + """Client writes a characteristic using WRITE_NO_RESPONSE.""" + server = circuitpython1 + client = circuitpython2 + + client.wait_until_done() + server.wait_until_done() + + server_output = server.serial.all_output + client_output = client.serial.all_output + + assert "char_b final [99]" in server_output + assert "initial [20]" in client_output + assert "wrote wnr" in client_output + + +# =================================================================== +# Test 5: Multiple characteristics on one service +# =================================================================== + +BSIM_MULTI_CHAR_CLIENT_CODE = """\ +import time +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from multi_service import MultiService + +ble = BLERadio() + +target = None +for adv in ble.start_scan(ProvideServicesAdvertisement, timeout=6.0): + if adv.connectable: + target = adv + break +ble.stop_scan() + +if target is None: + raise RuntimeError("No server found") + +connection = ble.connect(target, timeout=5.0) + +svc = connection[MultiService] +print("num chars", 3) + +# Read each characteristic +print("char", hex(0x2A19), list(svc.char_a)) +print("char", hex(0x2A1A), list(svc.char_b)) +print("char", hex(0x2A1B), list(svc.char_c)) + +# Write to the second characteristic +svc.char_b = bytes([77]) +print("wrote 0x2a1a") + +time.sleep(0.5) + +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while connection.connected and time.monotonic() < timeout: + time.sleep(0.1) +print("done") +""" + + +@pytest.mark.duration(25) +@pytest.mark.circuitpy_drive( + {"code.py": BSIM_MULTI_CHAR_SERVER_CODE, "multi_service.py": MULTI_LIB, **_ADAFRUIT_BLE} +) +@pytest.mark.circuitpy_drive( + {"code.py": BSIM_MULTI_CHAR_CLIENT_CODE, "multi_service.py": MULTI_LIB, **_ADAFRUIT_BLE} +) +def test_bsim_service_multi_char(bsim_phy, circuitpython1, circuitpython2): + """Service with three characteristics: discover all, read each, write one.""" + server = circuitpython1 + client = circuitpython2 + + client.wait_until_done() + server.wait_until_done() + + server_output = server.serial.all_output + client_output = client.serial.all_output + + assert "num chars 3" in client_output + assert "char 0x2a19 [10]" in client_output + assert "char 0x2a1a [20]" in client_output + assert "char 0x2a1b [30]" in client_output + assert "wrote 0x2a1a" in client_output + assert "char_b final [77]" in server_output + + +# =================================================================== +# Test 6: 128-bit custom UUID +# =================================================================== + +CUSTOM_LIB = """ +from adafruit_ble.services import Service +from adafruit_ble.characteristics import Characteristic, Attribute +from adafruit_ble.uuid import VendorUUID + + +class CustomService(Service): + uuid = VendorUUID("12345678-1234-5678-1234-56789abcdef0") + data = Characteristic( + uuid=VendorUUID("12345678-1234-5678-1234-56789abcdef1"), + properties=Characteristic.READ | Characteristic.WRITE, + read_perm=Attribute.OPEN, + write_perm=Attribute.OPEN, + max_length=4, + fixed_length=False, + initial_value=bytes([0xDE, 0xAD]), + )""" + +BSIM_CUSTOM_UUID_SERVER_CODE = """\ +import time +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from custom_service import CustomService + +ble = BLERadio() +svc = CustomService() +print("service created") + +advertisement = ProvideServicesAdvertisement(svc) +ble.start_advertising(advertisement) + +for _ in range(80): + if ble.connected: + break + time.sleep(0.1) +print("connected", ble.connected) + +for _ in range(80): + if not ble.connected: + break + time.sleep(0.1) + +print("final value", list(svc.data)) +print("done") +""" + +BSIM_CUSTOM_UUID_CLIENT_CODE = """\ +import time +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from custom_service import CustomService + +ble = BLERadio() + +target = None +for adv in ble.start_scan(ProvideServicesAdvertisement, timeout=6.0): + if adv.connectable: + target = adv + break +ble.stop_scan() + +if target is None: + raise RuntimeError("No server found") + +connection = ble.connect(target, timeout=5.0) + +svc = connection[CustomService] +print("discovered services", 1) + +print("char value", list(svc.data)) + +svc.data = bytes([0xBE, 0xEF]) +print("wrote custom") + +time.sleep(0.5) + +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while connection.connected and time.monotonic() < timeout: + time.sleep(0.1) +print("done") +""" + + +@pytest.mark.duration(25) +@pytest.mark.circuitpy_drive( + {"code.py": BSIM_CUSTOM_UUID_SERVER_CODE, "custom_service.py": CUSTOM_LIB, **_ADAFRUIT_BLE} +) +@pytest.mark.circuitpy_drive( + {"code.py": BSIM_CUSTOM_UUID_CLIENT_CODE, "custom_service.py": CUSTOM_LIB, **_ADAFRUIT_BLE} +) +def test_bsim_service_custom_uuid(bsim_phy, circuitpython1, circuitpython2): + """128-bit custom UUID service: discover, read, and write.""" + server = circuitpython1 + client = circuitpython2 + + client.wait_until_done() + server.wait_until_done() + + server_output = server.serial.all_output + client_output = client.serial.all_output + + assert "discovered services 1" in client_output + assert "char value [222, 173]" in client_output # 0xDE, 0xAD + assert "wrote custom" in client_output + assert "final value [190, 239]" in server_output # 0xBE, 0xEF + + +# =================================================================== +# Test 7: Empty discovery result +# =================================================================== + +BSIM_EMPTY_DISC_CLIENT_CODE = """\ +import time +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from heart_rate import HeartRateService + +ble = BLERadio() + +target = None +for adv in ble.start_scan(ProvideServicesAdvertisement, timeout=6.0): + if adv.connectable: + target = adv + print("found server") + break +ble.stop_scan() +print("have target", target is not None) + +if target is None: + raise RuntimeError("No server found") + +connection = ble.connect(target, timeout=5.0) +print("connected", connection.connected) + +# Ask for Heart Rate Service which doesn't exist on this server +found = HeartRateService in connection +print("found services", 1 if found else 0) + +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while connection.connected and time.monotonic() < timeout: + time.sleep(0.1) +print("done") +""" + + +@pytest.mark.duration(30) +@pytest.mark.circuitpy_drive( + {"code.py": BSIM_CUSTOM_UUID_SERVER_CODE, "custom_service.py": CUSTOM_LIB, **_ADAFRUIT_BLE} +) +@pytest.mark.circuitpy_drive( + {"code.py": BSIM_EMPTY_DISC_CLIENT_CODE, "heart_rate.py": HEART_RATE_LIB, **_ADAFRUIT_BLE} +) +def test_bsim_service_empty_discovery(bsim_phy, circuitpython1, circuitpython2): + """Filter for a UUID that doesn't exist, verify empty tuple returned.""" + server = circuitpython1 + client = circuitpython2 + + client.wait_until_done() + server.wait_until_done() + + client_output = client.serial.all_output + + assert "found services 0" in client_output + assert "done" in client_output diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_uuid.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_uuid.py new file mode 100644 index 00000000000..9dcf3b13847 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_uuid.py @@ -0,0 +1,331 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""BLE UUID tests for bsim.""" + +import pytest + + +# --- 16-bit UUIDs --- + +BSIM_UUID_16BIT_CODE = """\ +import _bleio + +u16 = _bleio.UUID(0x180F) +print("uuid16", u16.uuid16) +print("size", u16.size) +print("str", str(u16)) +print("repr", repr(u16)) +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_UUID_16BIT_CODE}) +def test_bsim_uuid_16bit_basic(bsim_phy, circuitpython): + """Construct a 16-bit UUID and verify all properties.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "uuid16 6159" in output # 0x180F = 6159 + assert "size 16" in output + assert "UUID(0x180f)" in output + assert "done" in output + + +BSIM_UUID_16BIT_EQ_CODE = """\ +import _bleio + +a = _bleio.UUID(0x180F) +b = _bleio.UUID(0x180F) +c = _bleio.UUID(0x180D) + +print("eq_same", a == b) +print("eq_diff", a == c) +print("neq_diff", a != c) +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_UUID_16BIT_EQ_CODE}) +def test_bsim_uuid_16bit_equality(bsim_phy, circuitpython): + """16-bit UUID equality: same value is equal, different values are not.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "eq_same True" in output + assert "eq_diff False" in output + assert "neq_diff True" in output + assert "done" in output + + +BSIM_UUID_16BIT_HASH_CODE = """\ +import _bleio + +a = _bleio.UUID(0x180F) +b = _bleio.UUID(0x180F) + +# Same UUID should hash the same +print("hash_same", hash(a) == hash(b)) + +# Can be used as dict keys +d = {a: "battery"} +print("dict_lookup", d[_bleio.UUID(0x180F)]) +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_UUID_16BIT_HASH_CODE}) +def test_bsim_uuid_16bit_hash(bsim_phy, circuitpython): + """16-bit UUID can be hashed and used as dict keys.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "hash_same True" in output + assert "dict_lookup battery" in output + assert "done" in output + + +BSIM_UUID_16BIT_PACK_CODE = """\ +import _bleio + +u16 = _bleio.UUID(0x180F) +buf = bytearray(4) +u16.pack_into(buf) +print("packed", list(buf[:2])) +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_UUID_16BIT_PACK_CODE}) +def test_bsim_uuid_16bit_pack_into(bsim_phy, circuitpython): + """pack_into for 16-bit UUID writes 2 bytes in little-endian.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + # 0x180F in little-endian: byte[0]=0x0F, byte[1]=0x18 + assert "packed [15, 24]" in output # 0x0F=15, 0x18=24 + assert "done" in output + + +BSIM_UUID_16BIT_PACK_OFFSET_CODE = """\ +import _bleio + +u16 = _bleio.UUID(0x2A19) +buf = bytearray(6) +u16.pack_into(buf, offset=4) +print("packed", list(buf)) +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_UUID_16BIT_PACK_OFFSET_CODE}) +def test_bsim_uuid_16bit_pack_into_offset(bsim_phy, circuitpython): + """pack_into with offset writes at the correct position.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + # 0x2A19 = 10777, little-endian: [0x19, 0x2A] = [25, 42] + assert "packed [0, 0, 0, 0, 25, 42]" in output + assert "done" in output + + +# --- 128-bit UUID string parsing --- + +BSIM_UUID_128BIT_STR_CODE = """\ +import _bleio + +u128 = _bleio.UUID("12345678-1234-5678-1234-56789abcdef0") +print("uuid16", u128.uuid16) +print("size", u128.size) +print("uuid128_len", len(u128.uuid128)) +# Bytes 12-13 are zeroed by shared-bindings (extracted as uuid16) +# First 4 bytes of uuid128 are [f0, de, bc, 9a] in LE +print("bytes_0_3", list(u128.uuid128[:4])) +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_UUID_128BIT_STR_CODE}) +def test_bsim_uuid_128bit_string(bsim_phy, circuitpython): + """Construct a 128-bit UUID from a hex string.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "size 128" in output + # uuid16 extracted from bytes 12-13: 0x5678 = 22136 + assert "uuid16 22136" in output + assert "uuid128_len 16" in output + # Bytes 12-13 are restored from uuid16 by common_hal_bleio_uuid_construct + # First 4 bytes of uuid128 are [f0, de, bc, 9a] in LE + assert "bytes_0_3 [240, 222, 188, 154]" in output + assert "done" in output + + +# --- 128-bit UUID bytes construction --- + +BSIM_UUID_128BIT_BYTES_CODE = """\ +import _bleio + +raw = bytes([0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12, + 0x34, 0x12, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56]) +u128 = _bleio.UUID(raw) +print("size", u128.size) +# uuid16 extracted from raw[12:14] = [0x34, 0x12] → 0x1234 = 4660 +print("uuid16", u128.uuid16) +print("uuid128_len", len(u128.uuid128)) +# Bytes 12-13 are restored from uuid16 by common_hal_bleio_uuid_construct +print("bytes_12_13", list(u128.uuid128[12:14])) +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_UUID_128BIT_BYTES_CODE}) +def test_bsim_uuid_128bit_bytes(bsim_phy, circuitpython): + """Construct a 128-bit UUID from a 16-byte buffer.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "size 128" in output + assert "uuid16 4660" in output + assert "uuid128_len 16" in output + # Bytes 12-13 restored from uuid16: 0x1234 → [0x34, 0x12] = [52, 18] + assert "bytes_12_13 [52, 18]" in output + assert "done" in output + + +# --- 128-bit UUID equality --- + +BSIM_UUID_128BIT_EQ_CODE = """\ +import _bleio + +a = _bleio.UUID("12345678-1234-5678-1234-56789abcdef0") +b = _bleio.UUID("12345678-1234-5678-1234-56789abcdef0") +c = _bleio.UUID("00000000-0000-1000-8000-00805f9b34fb") + +print("eq_same", a == b) +print("eq_diff", a == c) +print("neq_diff", a != c) +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_UUID_128BIT_EQ_CODE}) +def test_bsim_uuid_128bit_equality(bsim_phy, circuitpython): + """128-bit UUID equality: same bytes equal, different not.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "eq_same True" in output + assert "eq_diff False" in output + assert "neq_diff True" in output + assert "done" in output + + +# --- 16-bit vs 128-bit UUID equality (should not be equal even if same value) --- + +BSIM_UUID_CROSS_SIZE_EQ_CODE = """\ +import _bleio + +# 0x180F as 16-bit +u16 = _bleio.UUID(0x180F) + +# 0x180F expanded to 128-bit base UUID +# Bluetooth base: 00000000-0000-1000-8000-00805F9B34FB +# With 0x180F: 0000180F-0000-1000-8000-00805F9B34FB +u128 = _bleio.UUID("0000180f-0000-1000-8000-00805f9b34fb") + +print("size_16", u16.size) +print("size_128", u128.size) +# Per spec, 16-bit and 128-bit are NOT equal even if values match +print("cross_eq", u16 == u128) +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_UUID_CROSS_SIZE_EQ_CODE}) +def test_bsim_uuid_cross_size_equality(bsim_phy, circuitpython): + """16-bit and 128-bit UUIDs are not equal even with same value.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "size_16 16" in output + assert "size_128 128" in output + assert "cross_eq False" in output + assert "done" in output + + +# --- Invalid UUID string --- + +BSIM_UUID_INVALID_STR_CODE = """\ +import _bleio + +try: + u = _bleio.UUID("not-a-uuid") + print("should not reach") +except ValueError as e: + print("value_error", "not" in str(e) or "UUID" in str(e)) +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_UUID_INVALID_STR_CODE}) +def test_bsim_uuid_invalid_string(bsim_phy, circuitpython): + """Invalid UUID string raises ValueError.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "value_error True" in output + assert "done" in output + + +# --- UUID 128-bit: uuid128 property works, uuid16 returns 16-bit part --- + +BSIM_UUID_128BIT_PROPS_CODE = """\ +import _bleio + +u128 = _bleio.UUID("12345678-1234-5678-1234-56789abcdef0") +print("size", u128.size) +# uuid128 returns 16 bytes (bytes 12-13 are restored from uuid16) +b = u128.uuid128 +print("uuid128_len", len(b)) +# uuid16 is the 16-bit part from bytes 12-13, extracted and then restored +print("uuid16", u128.uuid16) +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_UUID_128BIT_PROPS_CODE}) +def test_bsim_uuid_128bit_properties(bsim_phy, circuitpython): + """128-bit UUID exposes uuid128 bytes and uuid16 from bytes 12-13.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "size 128" in output + assert "uuid128_len 16" in output + assert "uuid16 22136" in output + assert "done" in output + + +# --- 16-bit uuid128 raises AttributeError --- + +BSIM_UUID_16BIT_NO_UUID128_CODE = """\ +import _bleio + +u16 = _bleio.UUID(0x180F) +try: + _ = u16.uuid128 + print("should not reach") +except AttributeError: + print("attr_error ok") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_UUID_16BIT_NO_UUID128_CODE}) +def test_bsim_uuid_16bit_no_uuid128(bsim_phy, circuitpython): + """Accessing uuid128 on a 16-bit UUID raises AttributeError.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "attr_error ok" in output + assert "done" in output diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_workflow_advertising.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_workflow_advertising.py new file mode 100644 index 00000000000..a6e9bc6e0dd --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_workflow_advertising.py @@ -0,0 +1,112 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Advertising test for the supervisor BLE workflow (bsim). + +The supervisor's built-in BLE workflow (file-transfer + serial services and +advertising) starts automatically at boot, independent of user code. One +CircuitPython device runs an idle code.py so its workflow advertises; a second +CircuitPython device scans with the adafruit_ble library and verifies the +workflow's advertisement carries the File Transfer service UUID (0xFEBB) and +the CIRCUITPY device name. + +The second device disables its own workflow via settings.toml so only the +first device advertises. +""" + +import logging +import re + +import pytest + +from .conftest import get_library_files + +logger = logging.getLogger(__name__) + +_ADAFRUIT_BLE = get_library_files("adafruit_ble") + +# Device 1: idle code.py. The supervisor workflow advertises during the sleep. +WORKFLOW_IDLE_CODE = """\ +import time +time.sleep(15) +""" + +# Device 2: scan for the workflow advertisement. The workflow puts the +# File Transfer service UUID (0xFEBB) in the primary advertisement and the full +# "CIRCUITPYxxxx" name in the scan response, so those arrive as separate +# Advertisement objects (service list vs. scan response). +SCANNER_CODE = """\ +import time +from adafruit_ble import BLERadio +from adafruit_ble.advertising import Advertisement +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from adafruit_ble.uuid import StandardUUID + +ble = BLERadio() +print("scan start") +found_ft = False +found_name = False +ft_rssi = None +for adv in ble.start_scan(ProvideServicesAdvertisement, Advertisement, timeout=15, active=True): + if isinstance(adv, ProvideServicesAdvertisement) and StandardUUID(0xFEBB) in adv.services: + found_ft = True + ft_rssi = adv.rssi + print("ft_uuid febb") + name = adv.complete_name or adv.short_name or "" + if name: + found_name = True + print("cp_name", name) + if found_ft and found_name: + print("workflow found") + print("ft_rssi", ft_rssi) + break +ble.stop_scan() +print("scan done", found_ft, found_name, ft_rssi) +""" + +# Disable the workflow on the scanner so it doesn't advertise alongside device 1. +SCANNER_SETTINGS = "CIRCUITPY_BLE_WORKFLOW = false\n" + + +@pytest.mark.duration(20) +@pytest.mark.circuitpy_drive({"code.py": WORKFLOW_IDLE_CODE}) +@pytest.mark.circuitpy_drive( + { + "code.py": SCANNER_CODE, + "settings.toml": SCANNER_SETTINGS, + **_ADAFRUIT_BLE, + } +) +def test_bsim_workflow_advertises(bsim_phy, circuitpython1, circuitpython2): + """The supervisor BLE workflow advertises 0xFEBB and the CIRCUITPY name.""" + workflow = circuitpython1 + scanner = circuitpython2 + + scanner.wait_until_done() + + scanner_output = scanner.serial.all_output + assert "ft_uuid febb" in scanner_output, ( + f"File Transfer service UUID 0xFEBB not observed: {scanner_output}" + ) + # The device name currently advertises as the controller default ("Zephyr"); + # the CIRCUITPY name is configured separately, at which point this will + # observe the full "CIRCUITPY..." name. Either way a name must be present. + assert "cp_name" in scanner_output, f"device name not observed: {scanner_output}" + assert "workflow found" in scanner_output, ( + f"workflow advertisement not fully observed: {scanner_output}" + ) + + # The workflow advertises its public advert at reduced TX power (-20 dBm), + # so with the 40 dB channel attenuation the RSSI is ~ -59, well below the + # 0 dBm baseline of -39 seen for user advertisements. + rssi_match = re.search(r"ft_rssi (-?\d+)", scanner_output) + assert rssi_match, f"ft_rssi not observed: {scanner_output}" + ft_rssi = int(rssi_match.group(1)) + logger.info("workflow ft_rssi: %d", ft_rssi) + assert ft_rssi < -39, f"Expected reduced-TX-power RSSI (< -39), got {ft_rssi}" + + # The workflow device should not have entered safe mode. + workflow_output = workflow.serial.all_output + assert "safe mode" not in workflow_output.lower(), ( + f"workflow device entered safe mode: {workflow_output}" + ) diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_workflow_file_transfer.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_workflow_file_transfer.py new file mode 100644 index 00000000000..a7c383febe1 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_workflow_file_transfer.py @@ -0,0 +1,102 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""File Transfer service test for the supervisor BLE workflow (bsim). + +A second CircuitPython device connects to the supervisor's BLE workflow +(advertised with the File Transfer service UUID 0xFEBB), pairs, and uses the +Adafruit_CircuitPython_BLE_File_Transfer library to list the workflow device's +root directory. +""" + +import pytest + +from .conftest import get_library_files + +_ADAFRUIT_BLE = get_library_files("adafruit_ble") +_ADAFRUIT_BLE_FILE_TRANSFER = get_library_files("adafruit_ble_file_transfer") + +# Device 1: idle code.py; its workflow exposes the File Transfer service. +WORKFLOW_IDLE_CODE = """\ +import time +time.sleep(25) +""" + +# Device 2: connect, pair, and listdir("/") over the File Transfer service. +CLIENT_CODE = """\ +import time +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from adafruit_ble.uuid import StandardUUID +from adafruit_ble_file_transfer import FileTransferService, FileTransferClient + +ble = BLERadio() + +# Wait for the workflow device's file transfer server to finish starting up +# before scanning. +time.sleep(5) +print("scan start") +target = None +for adv in ble.start_scan(ProvideServicesAdvertisement, timeout=15, active=True): + if StandardUUID(0xFEBB) in adv.services: + target = adv + print("found workflow") + break +ble.stop_scan() +if target is None: + print("no workflow") + raise SystemExit(1) + +connection = ble.connect(target, timeout=10) +print("connected", connection.connected) +connection.pair() +print("paired", connection.paired) + +service = connection[FileTransferService] +print("ft version", service.version) +client = FileTransferClient(service) + +entries = client.listdir("/") +names = [e[0] for e in entries] +print("ft names", names) +if "code.py" in names: + print("ft code.py listed") +print("ft done") +connection.disconnect() +""" + +CLIENT_SETTINGS = "CIRCUITPY_BLE_WORKFLOW = false\n" + + +@pytest.mark.duration(30) +@pytest.mark.circuitpy_drive({"code.py": WORKFLOW_IDLE_CODE}) +@pytest.mark.circuitpy_drive( + { + "code.py": CLIENT_CODE, + "settings.toml": CLIENT_SETTINGS, + **_ADAFRUIT_BLE_FILE_TRANSFER, + **_ADAFRUIT_BLE, + } +) +def test_bsim_workflow_file_transfer(board, bsim_phy, circuitpython1, circuitpython2): + """The supervisor BLE workflow's File Transfer service can list files.""" + workflow = circuitpython1 + client = circuitpython2 + + client.wait_until_done() + + client_output = client.serial.all_output + assert "found workflow" in client_output, f"client never found the workflow: {client_output}" + assert "paired True" in client_output, f"pairing did not succeed: {client_output}" + assert "ft version 4" in client_output, ( + f"File Transfer version characteristic is not 4: {client_output}" + ) + assert "ft code.py listed" in client_output, ( + f"listdir did not include code.py: {client_output}" + ) + assert "ft done" in client_output, f"file transfer did not complete: {client_output}" + + workflow_output = workflow.serial.all_output + assert "safe mode" not in workflow_output.lower(), ( + f"workflow device entered safe mode: {workflow_output}" + ) diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_workflow_nus.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_workflow_nus.py new file mode 100644 index 00000000000..ebc0bbcc69c --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_workflow_nus.py @@ -0,0 +1,146 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Serial (Nordic-UART-style) service test for the supervisor BLE workflow +(bsim). + +The supervisor's BLE workflow exposes a CircuitPython serial service whose RX +characteristic accepts console stdin (write, encrypted) and whose TX +characteristic emits console stdout via notifications (encrypted). One +CircuitPython device runs code.py that reads a line with `input()` and echoes +it back with `print()`; a second CircuitPython device connects, pairs, +subscribes to TX, writes a line to RX, and verifies the echo comes back over TX. +""" + +import pytest + +from .conftest import get_library_files + +_ADAFRUIT_BLE = get_library_files("adafruit_ble") + +# Device 1: read a line from console stdin (BLE RX) and echo it to console +# stdout (mirrored to BLE TX). `input()` blocks until the central writes, so the +# device stays alive for the exchange. +WORKFLOW_CODE = """\ +import time +print("nus ready") +try: + line = input() +except EOFError: + line = "" +print("nus_echo:" + line) +# Keep the VM alive briefly so the BLE TX notification carrying the echo is +# actually transmitted over the link before the device exits. Without this the +# notification is queued but never sent because the process shuts down right +# after code.py finishes. +time.sleep(3) +""" + +# Device 2: connect, pair, discover the CircuitPython serial service, subscribe +# to TX, write a line to RX, and read the echo back over TX. +CLIENT_CODE = """\ +import time +import _bleio +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from adafruit_ble.uuid import StandardUUID + +ble = BLERadio() + +# Wait for the workflow device's serial service to finish starting up +# before scanning. Connecting too soon after the workflow's BLE init races +# with a Zephyr connection-parameter update and the link drops shortly +# after pairing. +time.sleep(5) +print("scan start") +target = None +for adv in ble.start_scan(ProvideServicesAdvertisement, timeout=15, active=True): + if StandardUUID(0xFEBB) in adv.services: + target = adv + print("found workflow") + break +ble.stop_scan() +if target is None: + print("no workflow") + raise SystemExit(1) + +connection = ble.connect(target, timeout=10) +print("connected", connection.connected) +connection.pair() +print("paired", connection.paired) + +# CircuitPython serial service UUID (128-bit, "nhtyPtiucriC" base + 0x0001). +serial_uuid = _bleio.UUID(b"nhtyPtiucriC\\x01\\x00\\xaf\\xad") +services = connection._bleio_connection.discover_remote_services([serial_uuid]) +svc = services[0] +print("serial service", svc.uuid.uuid16) + +rx_char = None +tx_char = None +for ch in svc.characteristics: + if ch.uuid.uuid16 == 0x0002: + rx_char = ch + elif ch.uuid.uuid16 == 0x0003: + tx_char = ch +print("rx", rx_char is not None, "tx", tx_char is not None) + +# Subscribe to TX notifications (client-side PacketBuffer writes the CCCD). +tx_pb = _bleio.PacketBuffer(tx_char, buffer_size=4, max_packet_size=128) + +# Write a line to RX (console stdin). RX has WRITE_NO_RESPONSE, so `.value =` +# does a GATT write without response. +rx_char.value = b"ZZZ\\r" +print("rx written") + +buf = bytearray(128) +got = b"" +deadline = time.monotonic() + 10 +while time.monotonic() < deadline: + n = tx_pb.readinto(buf) + if n: + got += bytes(buf[:n]) + if b"nus_echo:ZZZ" in got: + print("nus_received nus_echo:ZZZ") + break +print("nus done", b"nus_echo:ZZZ" in got) +connection.disconnect() +""" + +CLIENT_SETTINGS = "CIRCUITPY_BLE_WORKFLOW = false\n" + + +@pytest.mark.duration(30) +@pytest.mark.circuitpy_drive({"code.py": WORKFLOW_CODE}) +@pytest.mark.circuitpy_drive( + { + "code.py": CLIENT_CODE, + "settings.toml": CLIENT_SETTINGS, + **_ADAFRUIT_BLE, + } +) +def test_bsim_workflow_nus(board, bsim_phy, circuitpython1, circuitpython2): + """The supervisor BLE workflow's serial service echoes console I/O over BLE.""" + workflow = circuitpython1 + client = circuitpython2 + + client.wait_until_done() + + client_output = client.serial.all_output + assert "found workflow" in client_output, f"client never found the workflow: {client_output}" + assert "paired True" in client_output, f"pairing did not succeed: {client_output}" + assert "serial service 1" in client_output, ( + f"CircuitPython serial service not discovered: {client_output}" + ) + assert "rx True tx True" in client_output, ( + f"RX/TX characteristics not discovered: {client_output}" + ) + assert "rx written" in client_output, f"RX write failed: {client_output}" + assert "nus_received nus_echo:ZZZ" in client_output, ( + f"echo 'nus_echo:ZZZ' never received over TX: {client_output}" + ) + assert "nus done True" in client_output, f"echo not confirmed: {client_output}" + + workflow_output = workflow.serial.all_output + assert "safe mode" not in workflow_output.lower(), ( + f"workflow device entered safe mode: {workflow_output}" + ) diff --git a/ports/zephyr-cp/tests/conftest.py b/ports/zephyr-cp/tests/conftest.py index 03451048324..dcb63c1c1d9 100644 --- a/ports/zephyr-cp/tests/conftest.py +++ b/ports/zephyr-cp/tests/conftest.py @@ -289,27 +289,51 @@ def circuitpython(request, board, sim_id, native_sim_binary, native_sim_env, tmp tmp_drive = tmp_path / f"drive{i}" tmp_drive.mkdir(exist_ok=True) + fat_dirs_created = set() for name, content in files.items(): src = tmp_drive / name + src.parent.mkdir(parents=True, exist_ok=True) if isinstance(content, bytes): src.write_bytes(content) else: src.write_text(content) + # Create parent directories on the FAT image. + fat_dir = Path(name).parent + for fat_part in [*reversed(fat_dir.parents), fat_dir]: + if fat_part == Path("."): + continue + fat_path = "::" + str(fat_part) + if fat_path not in fat_dirs_created: + subprocess.run(["mmd", "-i", str(flash), fat_path], check=True) + fat_dirs_created.add(fat_path) subprocess.run(["mcopy", "-i", str(flash), str(src), f"::{name}"], check=True) trace_file = tmp_path / f"trace-{i}.perfetto" if "bsim" in board: - cmd = [str(native_sim_binary), f"--flash_app={flash}"] + # nRF54 bsim boards use RRAMC (--flash), others use NVMC (--flash_app) + flash_arg = "--flash" if "nrf54" in board else "--flash_app" + cmd = [str(native_sim_binary), f"{flash_arg}={flash}"] if instance_count > 1: cmd.append("-disconnect_on_exit=1") + # nRF54 bsim boards: console UART is SERIAL20 (bsim instance 1), others use instance 0 + uart_n = "1" if "nrf54" in board else "0" cmd.extend( ( f"-s={sim_id}", f"-d={i}", - "-uart0_pty", - "-uart0_pty_wait_for_readers", + f"-uart{uart_n}_pty", + f"-uart{uart_n}_pty_wait_for_readers", "-uart_pty_wait", + # Use the real AES implementation (libCryptov1.so) for the + # link layer instead of BabbleSim's plain-text stand-in. + # The nRF54L HW models route AES through several different + # stand-ins (ECB/CCM copy the data, CRACEN scrambles it), + # which are not consistent with each other, so LE + # encryption only works with real AES. Zephyr's own bsim + # encryption tests pass -RealEncryption=1 for the same + # reason. + "-RealEncryption=1", f"--vm-runs={code_py_runs + 1}", ) ) @@ -327,6 +351,10 @@ def circuitpython(request, board, sim_id, native_sim_binary, native_sim_env, tmp ) ) + # Always preserve retained memory (e.g. the safe-mode saved word) in + # in case of reboot. + cmd.append(f"--retained-memory={tmp_path / f'retained-{i}.bin'}") + if flash_erase_block_size is not None: cmd.append(f"--flash_erase_block_size={flash_erase_block_size}") if flash_write_block_size is not None: diff --git a/ports/zephyr-cp/tests/test_saved_word.py b/ports/zephyr-cp/tests/test_saved_word.py new file mode 100644 index 00000000000..0f93183f8e1 --- /dev/null +++ b/ports/zephyr-cp/tests/test_saved_word.py @@ -0,0 +1,42 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Test that the safe-mode saved word survives a hard reboot on native_sim/bsim. + +native_sim and the bsim boards reboot by re-executing the process (execv), +which wipes RAM (including .noinit). supervisor/port.c works around that by +persisting retained memory to a file across the reboot (the --retained-memory +flag); currently that holds the safe-mode saved word. + +The saved word drives safe-mode detection: ``microcontroller.on_next_reset( +RunMode.SAFE_MODE)`` arms the sentinel in the saved word, and a subsequent +``microcontroller.reset()`` reboots. If the word persisted, the next boot reads +the sentinel and enters safe mode (printed as "Running in safe mode!"). +""" + +import pytest + + +SAFE_MODE_RESET_CODE = """\ +import microcontroller +microcontroller.on_next_reset(microcontroller.RunMode.SAFE_MODE) +print("resetting") +microcontroller.reset() +""" + + +@pytest.mark.circuitpy_drive({"code.py": SAFE_MODE_RESET_CODE}) +@pytest.mark.duration(30) +def test_saved_word_survives_reboot_into_safe_mode(circuitpython): + """The saved word persists across a hard reboot and triggers safe mode.""" + circuitpython.serial.wait_for("resetting", timeout=20) + + # microcontroller.reset() re-execs the process; the UART PTY is O_CLOEXEC so + # a new one is opened after reboot. Reconnect to it. + assert circuitpython.reconnect_serial(timeout=20), "simulator did not reboot" + + # The next boot restores the saved word (the SAFE_MODE sentinel) and enters + # safe mode instead of running code.py. + circuitpython.serial.wait_for("Running in safe mode", timeout=20) + + assert "Running in safe mode" in circuitpython.serial.all_output diff --git a/ports/zephyr-cp/zephyr-config/west.yml b/ports/zephyr-cp/zephyr-config/west.yml index 4401481f966..889580d9f9b 100644 --- a/ports/zephyr-cp/zephyr-config/west.yml +++ b/ports/zephyr-cp/zephyr-config/west.yml @@ -4,10 +4,10 @@ manifest: projects: - name: nrf_hw_models url: https://github.com/tannewt/ext_nRF_hw_models - revision: c2927847bdf2ee0af9c5459f4155c67f39f6837a + revision: 41cb69cd08848cc31e91ed62d01d397511981919 path: modules/bsim_hw_models/nrf_hw_models - name: zephyr url: https://github.com/adafruit/zephyr - revision: e1dc85052bc8928572fdb972997c65eeb96f555b + revision: 499310df6e4da5ca54165c4b4c4c714771acb84c clone-depth: 100 import: true diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk old mode 100644 new mode 100755 index 955d872c751..af7b58111b6 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -405,6 +405,9 @@ endif ifeq ($(CIRCUITPY_STAGE),1) SRC_PATTERNS += _stage/% endif +ifeq ($(CIRCUITPY_PICOGAME),1) +SRC_PATTERNS += picogame/% +endif ifeq ($(CIRCUITPY_STORAGE),1) SRC_PATTERNS += storage/% endif @@ -483,6 +486,9 @@ endif ifeq ($(CIRCUITPY_WIFI),1) SRC_PATTERNS += wifi/% endif +ifeq ($(CIRCUITPY_WIZNET),1) +SRC_PATTERNS += wiznet/% +endif ifeq ($(CIRCUITPY_ZLIB),1) SRC_PATTERNS += zlib/% endif @@ -603,6 +609,12 @@ SRC_COMMON_HAL_ALL = \ wifi/ScannedNetworks.c \ wifi/__init__.c \ +# The fast Display backend is the only common-hal picogame source; include it +# only on ports that provide it (others use the portable bus.send renderer). +ifeq ($(CIRCUITPY_PICOGAME_FAST_DISPLAY),1) +SRC_COMMON_HAL_ALL += picogame/Display.c +endif + SRC_COMMON_HAL = $(filter $(SRC_PATTERNS), $(SRC_COMMON_HAL_ALL)) ifeq ($(CIRCUITPY_BLEIO_HCI),1) @@ -630,6 +642,11 @@ endif # All possible sources are listed here, and are filtered by SRC_PATTERNS. SRC_BINDINGS_ENUMS = \ $(filter $(SRC_PATTERNS), \ + picogame/Bitmap.c \ + picogame/Sprite.c \ + picogame/StripDraw.c \ + picogame/Triangles.c \ + picogame/Framebuffer.c \ _bleio/Address.c \ _bleio/Attribute.c \ _bleio/ScanEntry.c \ @@ -701,6 +718,11 @@ SRC_SHARED_MODULE_ALL = \ _stage/Layer.c \ _stage/Text.c \ _stage/__init__.c \ + picogame/__init__.c \ + picogame/Scene.c \ + picogame/Tilemap.c \ + picogame/Particles.c \ + picogame/Canvas.c \ aesio/__init__.c \ aesio/aes.c \ atexit/__init__.c \ @@ -712,6 +734,7 @@ SRC_SHARED_MODULE_ALL = \ audiospeed/__init__.c \ audiodelays/Echo.c \ audiodelays/Chorus.c \ + audiodelays/Flanger.c \ audiodelays/PitchShift.c \ audiodelays/GranularPitchShift.c \ audiodelays/MultiTapDelay.c \ diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 58d7fe74ea6..9dd5abc8a3a 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -530,6 +530,11 @@ void background_callback_run_all(void); #ifndef CIRCUITPY_SDCARD_USB #if CIRCUITPY_USB_DEVICE #define CIRCUITPY_SDCARD_USB (CIRCUITPY_SDCARDIO && CIRCUITPY_USB_MSC) +// Default value of CIRCUITPY_SDCARD_USB in settings.toml. +#ifndef CIRCUITPY_SDCARD_USB_DEFAULT +// True for most boards. +#define CIRCUITPY_SDCARD_USB_DEFAULT (true) +#endif #else #define CIRCUITPY_SDCARD_USB (0) #endif diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk old mode 100644 new mode 100755 index b6d88568d9c..855d40de40e --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -583,6 +583,28 @@ CFLAGS += -DCIRCUITPY_SSL_MBEDTLS=$(CIRCUITPY_SSL_MBEDTLS) CIRCUITPY_STAGE ?= 0 CFLAGS += -DCIRCUITPY_STAGE=$(CIRCUITPY_STAGE) +# PicoPad 2D game engine (off by default). +CIRCUITPY_PICOGAME ?= 0 +CFLAGS += -DCIRCUITPY_PICOGAME=$(CIRCUITPY_PICOGAME) +# Fast async-DMA Display backend: needs a port-specific common-hal (the raspberrypi and +# espressif ports provide one). Boards without it use the portable bus.send renderer. +CIRCUITPY_PICOGAME_FAST_DISPLAY ?= 0 +CFLAGS += -DCIRCUITPY_PICOGAME_FAST_DISPLAY=$(CIRCUITPY_PICOGAME_FAST_DISPLAY) +# Does this board's panel controller support 12-bit RGB444 (COLMOD)? A capability flag the +# board declares (ST7789/ST7735 = 1, ILI9341 = 0); exposed as picogame.RGB444_SUPPORTED so +# a game can enable Display(rgb444=...) only where it works. Default 0 (safe RGB565). +CIRCUITPY_PICOGAME_RGB444 ?= 0 +CFLAGS += -DCIRCUITPY_PICOGAME_RGB444=$(CIRCUITPY_PICOGAME_RGB444) +# Full-frame RAM-framebuffer render backend for scanout-buffer platforms (RP2350 DVI/HSTX +# boards like the Fruit Jam). Off by default so flash-tight SPI-only boards don't carry it. +CIRCUITPY_PICOGAME_FRAMEBUFFER ?= 0 +CFLAGS += -DCIRCUITPY_PICOGAME_FRAMEBUFFER=$(CIRCUITPY_PICOGAME_FRAMEBUFFER) +# Float vs 16.16 fixed path for the 3D helpers: the default follows the architecture +# (see shared-module/picogame/__init__.h); set 0/1 here only to override for a board. +ifneq ($(CIRCUITPY_PICOGAME_FPU),) +CFLAGS += -DCIRCUITPY_PICOGAME_FPU=$(CIRCUITPY_PICOGAME_FPU) +endif + CIRCUITPY_STATUS_BAR ?= 1 CFLAGS += -DCIRCUITPY_STATUS_BAR=$(CIRCUITPY_STATUS_BAR) @@ -757,6 +779,9 @@ CFLAGS += -DCIRCUITPY_WATCHDOG=$(CIRCUITPY_WATCHDOG) CIRCUITPY_WIFI ?= 0 CFLAGS += -DCIRCUITPY_WIFI=$(CIRCUITPY_WIFI) +CIRCUITPY_WIZNET ?= 0 +CFLAGS += -DCIRCUITPY_WIZNET=$(CIRCUITPY_WIZNET) + CIRCUITPY_WEB_WORKFLOW ?= $(CIRCUITPY_WIFI) CFLAGS += -DCIRCUITPY_WEB_WORKFLOW=$(CIRCUITPY_WEB_WORKFLOW) diff --git a/shared-bindings/_bleio/Adapter.c b/shared-bindings/_bleio/Adapter.c index a1f81a063fd..da8537c931e 100644 --- a/shared-bindings/_bleio/Adapter.c +++ b/shared-bindings/_bleio/Adapter.c @@ -8,11 +8,16 @@ #include #include "py/objproperty.h" +#include "py/objstr.h" #include "py/runtime.h" #include "shared-bindings/_bleio/__init__.h" #include "shared-bindings/_bleio/Address.h" #include "shared-bindings/_bleio/Adapter.h" +#if CIRCUITPY_SETTINGS_TOML +#include "supervisor/shared/settings.h" +#endif + #define ADV_INTERVAL_MIN (0.02f) #define ADV_INTERVAL_MIN_STRING "0.02" #define ADV_INTERVAL_MAX (10.24f) @@ -134,8 +139,11 @@ MP_PROPERTY_GETSET(bleio_adapter_address_obj, //| name: str //| """name of the BLE adapter used once connected. -//| The name is "CIRCUITPY" + the last four hex digits of ``adapter.address``, -//| to make it easy to distinguish multiple CircuitPython boards.""" +//| The default name is "CIRCUITPY" plus the last four hex digits of +//| ``adapter.address``, to make it easy to distinguish multiple +//| CircuitPython boards. Set ``CIRCUITPY_BLE_NAME`` in settings.toml to +//| override it. The default is re-applied each time the adapter is +//| enabled.""" //| static mp_obj_t _bleio_adapter_get_name(mp_obj_t self) { return MP_OBJ_FROM_PTR(common_hal_bleio_adapter_get_name(self)); @@ -153,6 +161,37 @@ MP_PROPERTY_GETSET(bleio_adapter_name_obj, (mp_obj_t)&bleio_adapter_get_name_obj, (mp_obj_t)&bleio_adapter_set_name_obj); +void bleio_adapter_reset_name(bleio_adapter_obj_t *self) { + #if CIRCUITPY_SETTINGS_TOML + // Let the user override the default name from settings.toml. Use the + // allocation-free settings_get_str (not os.getenv), because ports call + // this from common_hal_bleio_adapter_set_enabled() which can run before + // the VM/gc heap is initialized. + char ble_name[32]; + settings_err_t result = settings_get_str("CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name)); + if (result == SETTINGS_OK) { + common_hal_bleio_adapter_set_name(self, ble_name); + return; + } + #endif + + // Default to "CIRCUITPY" plus the last four hex digits of the adapter + // address so multiple boards are distinguishable. The address bytes are + // little-endian, so address[0] is the least-significant byte. If the port + // cannot provide an address, fall back to the literal "CIRCUITPYxxxx". + char default_ble_name[] = "CIRCUITPYxxxx"; + bleio_address_obj_t *address = common_hal_bleio_adapter_get_address(self); + if (address != NULL) { + uint8_t addr_bytes[NUM_BLEIO_ADDRESS_BYTES]; + common_hal_bleio_address_get_bytes(address, addr_bytes); + default_ble_name[ 9] = nibble_to_hex_lower[addr_bytes[1] >> 4 & 0xf]; + default_ble_name[10] = nibble_to_hex_lower[addr_bytes[1] & 0xf]; + default_ble_name[11] = nibble_to_hex_lower[addr_bytes[0] >> 4 & 0xf]; + default_ble_name[12] = nibble_to_hex_lower[addr_bytes[0] & 0xf]; + } + common_hal_bleio_adapter_set_name(self, default_ble_name); +} + //| def start_advertising( //| self, //| data: ReadableBuffer, diff --git a/shared-bindings/_bleio/Adapter.h b/shared-bindings/_bleio/Adapter.h index 0bc6f051ff3..1035461d35a 100644 --- a/shared-bindings/_bleio/Adapter.h +++ b/shared-bindings/_bleio/Adapter.h @@ -35,6 +35,13 @@ uint16_t bleio_adapter_get_name(char *buf, uint16_t len); extern mp_obj_str_t *common_hal_bleio_adapter_get_name(bleio_adapter_obj_t *self); extern void common_hal_bleio_adapter_set_name(bleio_adapter_obj_t *self, const char *name); +// Resets the adapter's device name to the default: the CIRCUITPY_BLE_NAME setting value if +// set, otherwise "CIRCUITPY" plus the last four hex digits of the adapter's address (read via +// common_hal_bleio_adapter_get_address). Every port calls this from its +// common_hal_bleio_adapter_set_enabled once the controller is ready, so the default name is +// derived the same way everywhere. +void bleio_adapter_reset_name(bleio_adapter_obj_t *self); + // Returns 0 if ok, otherwise a BLE stack specific error code. extern uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, bool anonymous, uint32_t timeout, float interval, diff --git a/shared-bindings/_bleio/Address.c b/shared-bindings/_bleio/Address.c index 10500bec9cb..ac5c47bec8a 100644 --- a/shared-bindings/_bleio/Address.c +++ b/shared-bindings/_bleio/Address.c @@ -108,10 +108,8 @@ static mp_obj_t bleio_address_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_o bleio_address_obj_t *lhs = MP_OBJ_TO_PTR(lhs_in); bleio_address_obj_t *rhs = MP_OBJ_TO_PTR(rhs_in); return mp_obj_new_bool( - mp_obj_equal(common_hal_bleio_address_get_address_bytes(lhs), - common_hal_bleio_address_get_address_bytes(rhs)) && - common_hal_bleio_address_get_type(lhs) == - common_hal_bleio_address_get_type(rhs)); + lhs->type == rhs->type && + memcmp(lhs->bytes, rhs->bytes, NUM_BLEIO_ADDRESS_BYTES) == 0); } else { return mp_const_false; @@ -130,13 +128,9 @@ static mp_obj_t bleio_address_unary_op(mp_unary_op_t op, mp_obj_t self_in) { switch (op) { // Two Addresses are equal if their address bytes and address_type are equal case MP_UNARY_OP_HASH: { - mp_obj_t bytes = common_hal_bleio_address_get_address_bytes(MP_OBJ_TO_PTR(self_in)); - GET_STR_HASH(bytes, h); - if (h == 0) { - GET_STR_DATA_LEN(bytes, data, len); - h = qstr_compute_hash(data, len); - } - h ^= common_hal_bleio_address_get_type(MP_OBJ_TO_PTR(self_in)); + bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t h = (mp_int_t)qstr_compute_hash(self->bytes, NUM_BLEIO_ADDRESS_BYTES); + h ^= self->type; return MP_OBJ_NEW_SMALL_INT(h); } default: @@ -146,11 +140,7 @@ static mp_obj_t bleio_address_unary_op(mp_unary_op_t op, mp_obj_t self_in) { static void bleio_address_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_buffer_info_t buf_info; - mp_obj_t address_bytes = common_hal_bleio_address_get_address_bytes(self); - mp_get_buffer_raise(address_bytes, &buf_info, MP_BUFFER_READ); - - const uint8_t *buf = (uint8_t *)buf_info.buf; + const uint8_t *buf = self->bytes; mp_printf(print, "
", buf[5], buf[4], buf[3], buf[2], buf[1], buf[0]); } diff --git a/shared-bindings/_bleio/Address.h b/shared-bindings/_bleio/Address.h index c869ccf55cb..a61f28c0369 100644 --- a/shared-bindings/_bleio/Address.h +++ b/shared-bindings/_bleio/Address.h @@ -20,6 +20,10 @@ extern const mp_obj_type_t bleio_address_type; -extern void common_hal_bleio_address_construct(bleio_address_obj_t *self, uint8_t *bytes, uint8_t address_type); +extern void common_hal_bleio_address_construct(bleio_address_obj_t *self, const uint8_t *bytes, uint8_t address_type); extern mp_obj_t common_hal_bleio_address_get_address_bytes(bleio_address_obj_t *self); extern uint8_t common_hal_bleio_address_get_type(bleio_address_obj_t *self); +// Copies the raw address bytes (little-endian BD_ADDR, NUM_BLEIO_ADDRESS_BYTES) +// into the given buffer. Allocation-free, so it is safe to use before the heap +// is initialized (e.g. when deriving the default adapter name). +extern void common_hal_bleio_address_get_bytes(bleio_address_obj_t *self, uint8_t bytes[NUM_BLEIO_ADDRESS_BYTES]); diff --git a/shared-bindings/_bleio/Service.c b/shared-bindings/_bleio/Service.c index cea9a68e694..920d66a3de2 100644 --- a/shared-bindings/_bleio/Service.c +++ b/shared-bindings/_bleio/Service.c @@ -8,6 +8,7 @@ #include "py/objproperty.h" #include "py/runtime.h" +#include "shared-bindings/_bleio/__init__.h" #include "shared-bindings/_bleio/Characteristic.h" #include "shared-bindings/_bleio/Service.h" #include "shared-bindings/_bleio/UUID.h" @@ -45,6 +46,9 @@ static mp_obj_t bleio_service_make_new(const mp_obj_type_t *type, size_t n_args, bleio_service_obj_t *service = mp_obj_malloc_with_finaliser(bleio_service_obj_t, &bleio_service_type); common_hal_bleio_service_construct(service, uuid, is_secondary); + // Remember that there are services that were created by the user. + // bleio_reset() needs this to know what kind of reset to do. + bleio_set_user_services_created(); return MP_OBJ_FROM_PTR(service); } diff --git a/shared-bindings/_bleio/__init__.c b/shared-bindings/_bleio/__init__.c index 1ec9fd96e76..202542c3d05 100644 --- a/shared-bindings/_bleio/__init__.c +++ b/shared-bindings/_bleio/__init__.c @@ -91,6 +91,20 @@ MP_NORETURN void mp_raise_bleio_SecurityError(mp_rom_error_text_t fmt, ...) { nlr_raise(exception); } +static bool _user_services_created; + +bool bleio_get_user_services_created(void) { + return _user_services_created; +} + +void bleio_set_user_services_created(void) { + _user_services_created = true; +} + +void bleio_clear_user_services_created(void) { + _user_services_created = false; +} + // Called when _bleio is imported. static mp_obj_t bleio___init__(void) { // HCI cannot be enabled on import, because we need to setup the HCI adapter first. diff --git a/shared-bindings/_bleio/__init__.h b/shared-bindings/_bleio/__init__.h index 0c5a5fec06b..0e8a9212e6b 100644 --- a/shared-bindings/_bleio/__init__.h +++ b/shared-bindings/_bleio/__init__.h @@ -39,6 +39,21 @@ void bleio_user_reset(void); // Completely resets the BLE stack including BLE connections. void bleio_reset(void); +// True if user code created a local GATT service during this VM run. Set by the +// shared-bindings Service constructor; the supervisor constructs its BLE workflow +// services through common-hal directly and does not set it. User-created services +// can only be removed from the stack's GATT table by a full stack reset, so a +// port's bleio_reset() uses this to decide whether that reset, which drops all +// connections, is needed at all. +bool bleio_get_user_services_created(void); + +// Record that user code created a local GATT service. +void bleio_set_user_services_created(void); + +// Clear the flag reported by bleio_get_user_services_created(). Call after a full +// stack reset, so the next VM run starts out clean. +void bleio_clear_user_services_created(void); + // Init any state needed before calling any bleio functions including those // having to do with bonding. This doesn't enable the BLE adapter though. void common_hal_bleio_init(void); @@ -50,6 +65,5 @@ MP_NORETURN void mp_raise_bleio_RoleError(mp_rom_error_text_t msg); MP_NORETURN void mp_raise_bleio_SecurityError(mp_rom_error_text_t msg, ...); bleio_adapter_obj_t *common_hal_bleio_allocate_adapter_or_raise(void); -void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist); void common_hal_bleio_gc_collect(void); diff --git a/shared-bindings/audiodelays/Echo.c b/shared-bindings/audiodelays/Echo.c index dbb496c61b9..f1c79de0e13 100644 --- a/shared-bindings/audiodelays/Echo.c +++ b/shared-bindings/audiodelays/Echo.c @@ -25,6 +25,7 @@ //| max_delay_ms: int = 500, //| delay_ms: synthio.BlockInput = 250.0, //| decay: synthio.BlockInput = 0.7, +//| filter: Optional[synthio.Biquad | Tuple[synthio.Biquad]] = None, //| mix: synthio.BlockInput = 0.25, //| buffer_size: int = 512, //| sample_rate: int = 8000, @@ -45,6 +46,7 @@ //| :param int max_delay_ms: The maximum time the echo can be in milliseconds //| :param synthio.BlockInput delay_ms: The current time of the echo delay in milliseconds. Must be less the max_delay_ms //| :param synthio.BlockInput decay: The rate the echo fades. 0.0 = instant; 1.0 = never. +//| :param Optional[synthio.Biquad|Tuple[synthio.Biquad]] filter: A normalized biquad filter object or tuple of normalized biquad filter objects. A copy of the samples are processed sequentially by each filter before writing into the echo buffer. The original samples are not affected by this argument. //| :param synthio.BlockInput mix: The mix as a ratio of the sample (0.0) to the effect (1.0). //| :param int buffer_size: The total size in bytes of each of the two playback buffers to use //| :param int sample_rate: The sample rate to be used @@ -76,11 +78,12 @@ //| ... //| static mp_obj_t audiodelays_echo_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - enum { ARG_max_delay_ms, ARG_delay_ms, ARG_decay, ARG_mix, ARG_buffer_size, ARG_sample_rate, ARG_bits_per_sample, ARG_samples_signed, ARG_channel_count, ARG_freq_shift, }; + enum { ARG_max_delay_ms, ARG_delay_ms, ARG_decay, ARG_filter, ARG_mix, ARG_buffer_size, ARG_sample_rate, ARG_bits_per_sample, ARG_samples_signed, ARG_channel_count, ARG_freq_shift, }; static const mp_arg_t allowed_args[] = { { MP_QSTR_max_delay_ms, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 500 } }, { MP_QSTR_delay_ms, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_decay, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_filter, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } }, { MP_QSTR_mix, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_buffer_size, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 512} }, { MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 8000} }, @@ -103,7 +106,7 @@ static mp_obj_t audiodelays_echo_make_new(const mp_obj_type_t *type, size_t n_ar } audiodelays_echo_obj_t *self = mp_obj_malloc(audiodelays_echo_obj_t, &audiodelays_echo_type); - common_hal_audiodelays_echo_construct(self, max_delay_ms, args[ARG_delay_ms].u_obj, args[ARG_decay].u_obj, args[ARG_mix].u_obj, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate, args[ARG_freq_shift].u_bool); + common_hal_audiodelays_echo_construct(self, max_delay_ms, args[ARG_delay_ms].u_obj, args[ARG_decay].u_obj, args[ARG_filter].u_obj, args[ARG_mix].u_obj, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate, args[ARG_freq_shift].u_bool); return MP_OBJ_FROM_PTR(self); } @@ -177,6 +180,29 @@ MP_PROPERTY_GETSET(audiodelays_echo_decay_obj, (mp_obj_t)&audiodelays_echo_get_decay_obj, (mp_obj_t)&audiodelays_echo_set_decay_obj); + +//| filter: synthio.Biquad | Tuple[synthio.Biquad] | None +//| """A normalized biquad filter object or tuple of normalized biquad filter objects. A copy of the samples are processed sequentially by each filter before writing into the echo buffer. The original samples are not affected by this property.""" +//| +static mp_obj_t audiodelays_echo_obj_get_filter(mp_obj_t self_in) { + audiodelays_echo_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return common_hal_audiodelays_echo_get_filter(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_echo_get_filter_obj, audiodelays_echo_obj_get_filter); + +static mp_obj_t audiodelays_echo_obj_set_filter(mp_obj_t self_in, mp_obj_t filter_in) { + audiodelays_echo_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiodelays_echo_set_filter(self, filter_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiodelays_echo_set_filter_obj, audiodelays_echo_obj_set_filter); + +MP_PROPERTY_GETSET(audiodelays_echo_filter_obj, + (mp_obj_t)&audiodelays_echo_get_filter_obj, + (mp_obj_t)&audiodelays_echo_set_filter_obj); + + //| mix: synthio.BlockInput //| """The rate the echo mix between 0 and 1 where 0 is only sample, 0.5 is an equal mix of the sample and the effect and 1 is all effect.""" static mp_obj_t audiodelays_echo_obj_get_mix(mp_obj_t self_in) { @@ -285,6 +311,7 @@ static const mp_rom_map_elem_t audiodelays_echo_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiodelays_echo_playing_obj) }, { MP_ROM_QSTR(MP_QSTR_delay_ms), MP_ROM_PTR(&audiodelays_echo_delay_ms_obj) }, { MP_ROM_QSTR(MP_QSTR_decay), MP_ROM_PTR(&audiodelays_echo_decay_obj) }, + { MP_ROM_QSTR(MP_QSTR_filter), MP_ROM_PTR(&audiodelays_echo_filter_obj) }, { MP_ROM_QSTR(MP_QSTR_mix), MP_ROM_PTR(&audiodelays_echo_mix_obj) }, { MP_ROM_QSTR(MP_QSTR_freq_shift), MP_ROM_PTR(&audiodelays_echo_freq_shift_obj) }, AUDIOSAMPLE_FIELDS, diff --git a/shared-bindings/audiodelays/Echo.h b/shared-bindings/audiodelays/Echo.h index 83d454ed05c..67b7436b896 100644 --- a/shared-bindings/audiodelays/Echo.h +++ b/shared-bindings/audiodelays/Echo.h @@ -11,7 +11,7 @@ extern const mp_obj_type_t audiodelays_echo_type; void common_hal_audiodelays_echo_construct(audiodelays_echo_obj_t *self, uint32_t max_delay_ms, - mp_obj_t delay_ms, mp_obj_t decay, mp_obj_t mix, + mp_obj_t delay_ms, mp_obj_t decay, mp_obj_t filter, mp_obj_t mix, uint32_t buffer_size, uint8_t bits_per_sample, bool samples_signed, uint8_t channel_count, uint32_t sample_rate, bool freq_shift); @@ -26,6 +26,9 @@ void common_hal_audiodelays_echo_set_freq_shift(audiodelays_echo_obj_t *self, bo mp_obj_t common_hal_audiodelays_echo_get_decay(audiodelays_echo_obj_t *self); void common_hal_audiodelays_echo_set_decay(audiodelays_echo_obj_t *self, mp_obj_t decay); +mp_obj_t common_hal_audiodelays_echo_get_filter(audiodelays_echo_obj_t *self); +void common_hal_audiodelays_echo_set_filter(audiodelays_echo_obj_t *self, mp_obj_t arg); + mp_obj_t common_hal_audiodelays_echo_get_mix(audiodelays_echo_obj_t *self); void common_hal_audiodelays_echo_set_mix(audiodelays_echo_obj_t *self, mp_obj_t arg); diff --git a/shared-bindings/audiodelays/Flanger.c b/shared-bindings/audiodelays/Flanger.c new file mode 100644 index 00000000000..7c16e164944 --- /dev/null +++ b/shared-bindings/audiodelays/Flanger.c @@ -0,0 +1,389 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tim Cocks for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include + +#include "shared-bindings/audiodelays/Flanger.h" +#include "shared-module/audiodelays/Flanger.h" +#include "shared-bindings/audiocore/__init__.h" + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/util.h" +#include "shared-module/synthio/block.h" + +//| class Flanger: +//| """A Flanger effect""" +//| +//| def __init__( +//| self, +//| max_delay_ms: int = 10, +//| min_delay_ms: synthio.BlockInput = 1.0, +//| rate: synthio.BlockInput = 0.5, +//| depth: synthio.BlockInput = 0.5, +//| feedback: synthio.BlockInput = 0.5, +//| mix: synthio.BlockInput = 0.5, +//| invert: bool = False, +//| buffer_size: int = 512, +//| sample_rate: int = 8000, +//| bits_per_sample: int = 16, +//| samples_signed: bool = True, +//| channel_count: int = 1, +//| ) -> None: +//| """Create a Flanger effect by mixing the sample with a copy of itself taken from a very +//| short delay whose length is continuously swept up and down by an internal low frequency +//| oscillator. The moving delay creates a comb filter whose notches sweep through the +//| spectrum, and the ``feedback`` path routes the delayed signal back into the delay line +//| to sharpen those notches into resonant peaks. +//| +//| The delay is swept upwards from ``min_delay_ms`` towards ``max_delay_ms``:: +//| +//| sweep_top = min_delay_ms + depth * (max_delay_ms - min_delay_ms) +//| current_delay = min_delay_ms + triangle_lfo() * (sweep_top - min_delay_ms) +//| +//| where ``triangle_lfo()`` ranges from 0.0 to 1.0 at ``rate`` Hz. Because the sweep runs +//| upwards from a floor rather than around a centre, no combination of ``min_delay_ms`` and +//| ``depth`` can push the delay outside of the buffer. +//| +//| +//| :param int max_delay_ms: The maximum delay the flanger can sweep to in milliseconds, valid range is 1-100. +//| :param synthio.BlockInput min_delay_ms: The shortest delay of the sweep in milliseconds. Clamped between the length of one sample and max_delay_ms. +//| :param synthio.BlockInput rate: The frequency of the sweep in hertz. Clamped between 0.0 and 20.0. A rate of 0.0 holds the delay still. +//| :param synthio.BlockInput depth: How much of the range above min_delay_ms is swept, from 0.0 to 1.0. +//| :param synthio.BlockInput feedback: How much of the delayed signal is fed back into the delay line, from -0.95 to 0.95. Negative values invert the polarity of the feedback. +//| :param synthio.BlockInput mix: How much of the wet audio to include along with the original signal, from 0.0 to 1.0. +//| :param bool invert: Subtract the wet signal from the dry signal instead of adding it, which flips which frequencies cancel. +//| :param int buffer_size: The total size in bytes of each of the two playback buffers to use +//| :param int sample_rate: The sample rate to be used +//| :param int channel_count: The number of channels the source samples contain. 1 = mono; 2 = stereo. +//| :param int bits_per_sample: The bits per sample of the effect +//| :param bool samples_signed: Effect is signed (True) or unsigned (False) +//| +//| Adding a flanger to a synth:: +//| +//| import time +//| import board +//| import audiobusio +//| import synthio +//| import audiodelays +//| +//| audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WS, board.I2S_DOUT) +//| synth = synthio.Synthesizer(channel_count=1, sample_rate=44100) +//| flanger = audiodelays.Flanger(max_delay_ms=10, min_delay_ms=1.0, rate=0.3, depth=0.8, +//| feedback=0.7, mix=1.0, buffer_size=1024, +//| channel_count=1, sample_rate=44100) +//| audio.play(flanger.play(synth)) +//| +//| note = synthio.Note(261) +//| while True: +//| synth.press(note) +//| time.sleep(3) +//| synth.release(note) +//| time.sleep(1)""" +//| ... +//| +static mp_obj_t audiodelays_flanger_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_max_delay_ms, ARG_min_delay_ms, ARG_rate, ARG_depth, ARG_feedback, ARG_mix, ARG_invert, ARG_buffer_size, ARG_sample_rate, ARG_bits_per_sample, ARG_samples_signed, ARG_channel_count, }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_max_delay_ms, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 10 } }, + { MP_QSTR_min_delay_ms, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_rate, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_depth, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_feedback, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_mix, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_invert, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_buffer_size, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 512} }, + { MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 8000} }, + { MP_QSTR_bits_per_sample, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 16} }, + { MP_QSTR_samples_signed, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + { MP_QSTR_channel_count, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1 } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_int_t max_delay_ms = mp_arg_validate_int_range(args[ARG_max_delay_ms].u_int, 1, 100, MP_QSTR_max_delay_ms); + + mp_int_t channel_count = mp_arg_validate_int_range(args[ARG_channel_count].u_int, 1, 2, MP_QSTR_channel_count); + mp_int_t sample_rate = mp_arg_validate_int_min(args[ARG_sample_rate].u_int, 1, MP_QSTR_sample_rate); + mp_int_t bits_per_sample = args[ARG_bits_per_sample].u_int; + if (bits_per_sample != 8 && bits_per_sample != 16) { + mp_raise_ValueError(MP_ERROR_TEXT("bits_per_sample must be 8 or 16")); + } + + audiodelays_flanger_obj_t *self = mp_obj_malloc(audiodelays_flanger_obj_t, &audiodelays_flanger_type); + common_hal_audiodelays_flanger_construct(self, max_delay_ms, args[ARG_min_delay_ms].u_obj, args[ARG_rate].u_obj, args[ARG_depth].u_obj, args[ARG_feedback].u_obj, args[ARG_mix].u_obj, args[ARG_invert].u_bool, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialises the Flanger.""" +//| ... +//| +static mp_obj_t audiodelays_flanger_deinit(mp_obj_t self_in) { + audiodelays_flanger_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiodelays_flanger_deinit(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_flanger_deinit_obj, audiodelays_flanger_deinit); + +static void check_for_deinit(audiodelays_flanger_obj_t *self) { + audiosample_check_for_deinit(&self->base); +} + +//| def __enter__(self) -> Flanger: +//| """No-op used by Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +static mp_obj_t audiodelays_flanger_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_audiodelays_flanger_deinit(args[0]); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audiodelays_flanger___exit___obj, 4, 4, audiodelays_flanger_obj___exit__); + + +//| min_delay_ms: synthio.BlockInput +//| """The shortest delay of the sweep in milliseconds. This is the floor that the sweep runs +//| upwards from. Clamped between the length of a single sample and ``max_delay_ms``.""" +//| +static mp_obj_t audiodelays_flanger_obj_get_min_delay_ms(mp_obj_t self_in) { + audiodelays_flanger_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return common_hal_audiodelays_flanger_get_min_delay_ms(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_flanger_get_min_delay_ms_obj, audiodelays_flanger_obj_get_min_delay_ms); + +static mp_obj_t audiodelays_flanger_obj_set_min_delay_ms(mp_obj_t self_in, mp_obj_t min_delay_ms_in) { + audiodelays_flanger_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiodelays_flanger_set_min_delay_ms(self, min_delay_ms_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiodelays_flanger_set_min_delay_ms_obj, audiodelays_flanger_obj_set_min_delay_ms); + +MP_PROPERTY_GETSET(audiodelays_flanger_min_delay_ms_obj, + (mp_obj_t)&audiodelays_flanger_get_min_delay_ms_obj, + (mp_obj_t)&audiodelays_flanger_set_min_delay_ms_obj); + +//| rate: synthio.BlockInput +//| """The frequency of the delay sweep in hertz. Clamped between 0.0 and 20.0. A rate of 0.0 +//| holds the delay still, which leaves a fixed comb filter.""" +//| +static mp_obj_t audiodelays_flanger_obj_get_rate(mp_obj_t self_in) { + return common_hal_audiodelays_flanger_get_rate(self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_flanger_get_rate_obj, audiodelays_flanger_obj_get_rate); + +static mp_obj_t audiodelays_flanger_obj_set_rate(mp_obj_t self_in, mp_obj_t rate_in) { + audiodelays_flanger_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiodelays_flanger_set_rate(self, rate_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiodelays_flanger_set_rate_obj, audiodelays_flanger_obj_set_rate); + +MP_PROPERTY_GETSET(audiodelays_flanger_rate_obj, + (mp_obj_t)&audiodelays_flanger_get_rate_obj, + (mp_obj_t)&audiodelays_flanger_set_rate_obj); + +//| depth: synthio.BlockInput +//| """How much of the range between ``min_delay_ms`` and ``max_delay_ms`` the sweep covers, from +//| 0.0 to 1.0. A depth of 0.0 leaves a static short delay.""" +//| +static mp_obj_t audiodelays_flanger_obj_get_depth(mp_obj_t self_in) { + return common_hal_audiodelays_flanger_get_depth(self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_flanger_get_depth_obj, audiodelays_flanger_obj_get_depth); + +static mp_obj_t audiodelays_flanger_obj_set_depth(mp_obj_t self_in, mp_obj_t depth_in) { + audiodelays_flanger_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiodelays_flanger_set_depth(self, depth_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiodelays_flanger_set_depth_obj, audiodelays_flanger_obj_set_depth); + +MP_PROPERTY_GETSET(audiodelays_flanger_depth_obj, + (mp_obj_t)&audiodelays_flanger_get_depth_obj, + (mp_obj_t)&audiodelays_flanger_set_depth_obj); + +//| feedback: synthio.BlockInput +//| """How much of the delayed signal is fed back into the delay line, clamped between -0.95 and +//| 0.95. Larger magnitudes give a more resonant, ringing sweep. Negative values invert the +//| polarity of the feedback for the hollower variant.""" +//| +static mp_obj_t audiodelays_flanger_obj_get_feedback(mp_obj_t self_in) { + return common_hal_audiodelays_flanger_get_feedback(self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_flanger_get_feedback_obj, audiodelays_flanger_obj_get_feedback); + +static mp_obj_t audiodelays_flanger_obj_set_feedback(mp_obj_t self_in, mp_obj_t feedback_in) { + audiodelays_flanger_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiodelays_flanger_set_feedback(self, feedback_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiodelays_flanger_set_feedback_obj, audiodelays_flanger_obj_set_feedback); + +MP_PROPERTY_GETSET(audiodelays_flanger_feedback_obj, + (mp_obj_t)&audiodelays_flanger_get_feedback_obj, + (mp_obj_t)&audiodelays_flanger_set_feedback_obj); + +//| mix: synthio.BlockInput +//| """How much of the wet audio to include along with the original signal, clamped between 0.0 +//| and 1.0 where 0.0 is only the sample and 1.0 is the full effect.""" +//| +static mp_obj_t audiodelays_flanger_obj_get_mix(mp_obj_t self_in) { + return common_hal_audiodelays_flanger_get_mix(self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_flanger_get_mix_obj, audiodelays_flanger_obj_get_mix); + +static mp_obj_t audiodelays_flanger_obj_set_mix(mp_obj_t self_in, mp_obj_t mix_in) { + audiodelays_flanger_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiodelays_flanger_set_mix(self, mix_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiodelays_flanger_set_mix_obj, audiodelays_flanger_obj_set_mix); + +MP_PROPERTY_GETSET(audiodelays_flanger_mix_obj, + (mp_obj_t)&audiodelays_flanger_get_mix_obj, + (mp_obj_t)&audiodelays_flanger_set_mix_obj); + +//| invert: bool +//| """Whether the wet signal is subtracted from the dry signal instead of added to it. This +//| flips which frequencies the comb filter cancels, the same as the polarity switch on a +//| flanger pedal.""" +//| +static mp_obj_t audiodelays_flanger_obj_get_invert(mp_obj_t self_in) { + audiodelays_flanger_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_audiodelays_flanger_get_invert(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_flanger_get_invert_obj, audiodelays_flanger_obj_get_invert); + +static mp_obj_t audiodelays_flanger_obj_set_invert(mp_obj_t self_in, mp_obj_t invert_in) { + audiodelays_flanger_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiodelays_flanger_set_invert(self, mp_obj_is_true(invert_in)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiodelays_flanger_set_invert_obj, audiodelays_flanger_obj_set_invert); + +MP_PROPERTY_GETSET(audiodelays_flanger_invert_obj, + (mp_obj_t)&audiodelays_flanger_get_invert_obj, + (mp_obj_t)&audiodelays_flanger_set_invert_obj); + +//| lfo_value: float +//| """The current value of the internal LFO that sweeps the delay time, from 0.0 at +//| `min_delay_ms` to 1.0 at the top of the sweep. The LFO only advances while audio is being +//| played. (read-only)""" +//| +static mp_obj_t audiodelays_flanger_obj_get_lfo_value(mp_obj_t self_in) { + audiodelays_flanger_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_float(common_hal_audiodelays_flanger_get_lfo_value(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_flanger_get_lfo_value_obj, audiodelays_flanger_obj_get_lfo_value); + +MP_PROPERTY_GETTER(audiodelays_flanger_lfo_value_obj, + (mp_obj_t)&audiodelays_flanger_get_lfo_value_obj); + +//| playing: bool +//| """True when the effect is playing a sample. (read-only)""" +//| +static mp_obj_t audiodelays_flanger_obj_get_playing(mp_obj_t self_in) { + audiodelays_flanger_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_audiodelays_flanger_get_playing(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_flanger_get_playing_obj, audiodelays_flanger_obj_get_playing); + +MP_PROPERTY_GETTER(audiodelays_flanger_playing_obj, + (mp_obj_t)&audiodelays_flanger_get_playing_obj); + +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> Flanger: +//| """Plays the sample once when loop=False and continuously when loop=True. +//| Does not block. Use `playing` to block. +//| +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: Flanger""" +//| ... +//| +static mp_obj_t audiodelays_flanger_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sample, ARG_loop }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED, {} }, + { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + audiodelays_flanger_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + + mp_obj_t sample = args[ARG_sample].u_obj; + common_hal_audiodelays_flanger_play(self, sample, args[ARG_loop].u_bool); + + return MP_OBJ_FROM_PTR(self); +} +MP_DEFINE_CONST_FUN_OBJ_KW(audiodelays_flanger_play_obj, 1, audiodelays_flanger_obj_play); + +//| def stop(self) -> None: +//| """Stops playback of the sample.""" +//| ... +//| +//| +static mp_obj_t audiodelays_flanger_obj_stop(mp_obj_t self_in) { + audiodelays_flanger_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_audiodelays_flanger_stop(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_flanger_stop_obj, audiodelays_flanger_obj_stop); + +static const mp_rom_map_elem_t audiodelays_flanger_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiodelays_flanger_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audiodelays_flanger___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audiodelays_flanger_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audiodelays_flanger_stop_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiodelays_flanger_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_min_delay_ms), MP_ROM_PTR(&audiodelays_flanger_min_delay_ms_obj) }, + { MP_ROM_QSTR(MP_QSTR_rate), MP_ROM_PTR(&audiodelays_flanger_rate_obj) }, + { MP_ROM_QSTR(MP_QSTR_depth), MP_ROM_PTR(&audiodelays_flanger_depth_obj) }, + { MP_ROM_QSTR(MP_QSTR_feedback), MP_ROM_PTR(&audiodelays_flanger_feedback_obj) }, + { MP_ROM_QSTR(MP_QSTR_mix), MP_ROM_PTR(&audiodelays_flanger_mix_obj) }, + { MP_ROM_QSTR(MP_QSTR_invert), MP_ROM_PTR(&audiodelays_flanger_invert_obj) }, + { MP_ROM_QSTR(MP_QSTR_lfo_value), MP_ROM_PTR(&audiodelays_flanger_lfo_value_obj) }, + AUDIOSAMPLE_FIELDS, +}; +static MP_DEFINE_CONST_DICT(audiodelays_flanger_locals_dict, audiodelays_flanger_locals_dict_table); + +static const audiosample_p_t audiodelays_flanger_proto = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_audiosample) + .reset_buffer = (audiosample_reset_buffer_fun)audiodelays_flanger_reset_buffer, + .get_buffer = (audiosample_get_buffer_fun)audiodelays_flanger_get_buffer, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + audiodelays_flanger_type, + MP_QSTR_Flanger, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, audiodelays_flanger_make_new, + locals_dict, &audiodelays_flanger_locals_dict, + protocol, &audiodelays_flanger_proto + ); diff --git a/shared-bindings/audiodelays/Flanger.h b/shared-bindings/audiodelays/Flanger.h new file mode 100644 index 00000000000..f139e842dbd --- /dev/null +++ b/shared-bindings/audiodelays/Flanger.h @@ -0,0 +1,43 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tim Cocks for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-module/audiodelays/Flanger.h" + +extern const mp_obj_type_t audiodelays_flanger_type; + +void common_hal_audiodelays_flanger_construct(audiodelays_flanger_obj_t *self, uint32_t max_delay_ms, + mp_obj_t min_delay_ms, mp_obj_t rate, mp_obj_t depth, mp_obj_t feedback, mp_obj_t mix, bool invert, + uint32_t buffer_size, uint8_t bits_per_sample, + bool samples_signed, uint8_t channel_count, uint32_t sample_rate); + +void common_hal_audiodelays_flanger_deinit(audiodelays_flanger_obj_t *self); +bool common_hal_audiodelays_flanger_deinited(audiodelays_flanger_obj_t *self); + +mp_obj_t common_hal_audiodelays_flanger_get_min_delay_ms(audiodelays_flanger_obj_t *self); +void common_hal_audiodelays_flanger_set_min_delay_ms(audiodelays_flanger_obj_t *self, mp_obj_t min_delay_ms); + +mp_obj_t common_hal_audiodelays_flanger_get_rate(audiodelays_flanger_obj_t *self); +void common_hal_audiodelays_flanger_set_rate(audiodelays_flanger_obj_t *self, mp_obj_t rate); + +mp_obj_t common_hal_audiodelays_flanger_get_depth(audiodelays_flanger_obj_t *self); +void common_hal_audiodelays_flanger_set_depth(audiodelays_flanger_obj_t *self, mp_obj_t depth); + +mp_obj_t common_hal_audiodelays_flanger_get_feedback(audiodelays_flanger_obj_t *self); +void common_hal_audiodelays_flanger_set_feedback(audiodelays_flanger_obj_t *self, mp_obj_t feedback); + +mp_obj_t common_hal_audiodelays_flanger_get_mix(audiodelays_flanger_obj_t *self); +void common_hal_audiodelays_flanger_set_mix(audiodelays_flanger_obj_t *self, mp_obj_t arg); + +mp_float_t common_hal_audiodelays_flanger_get_lfo_value(audiodelays_flanger_obj_t *self); + +bool common_hal_audiodelays_flanger_get_invert(audiodelays_flanger_obj_t *self); +void common_hal_audiodelays_flanger_set_invert(audiodelays_flanger_obj_t *self, bool invert); + +bool common_hal_audiodelays_flanger_get_playing(audiodelays_flanger_obj_t *self); +void common_hal_audiodelays_flanger_play(audiodelays_flanger_obj_t *self, mp_obj_t sample, bool loop); +void common_hal_audiodelays_flanger_stop(audiodelays_flanger_obj_t *self); diff --git a/shared-bindings/audiodelays/__init__.c b/shared-bindings/audiodelays/__init__.c index 2125aa54683..6ee7a82ab79 100644 --- a/shared-bindings/audiodelays/__init__.c +++ b/shared-bindings/audiodelays/__init__.c @@ -12,6 +12,7 @@ #include "shared-bindings/audiodelays/__init__.h" #include "shared-bindings/audiodelays/Echo.h" #include "shared-bindings/audiodelays/Chorus.h" +#include "shared-bindings/audiodelays/Flanger.h" #include "shared-bindings/audiodelays/PitchShift.h" #include "shared-bindings/audiodelays/GranularPitchShift.h" #include "shared-bindings/audiodelays/MultiTapDelay.h" @@ -26,6 +27,7 @@ static const mp_rom_map_elem_t audiodelays_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audiodelays) }, { MP_ROM_QSTR(MP_QSTR_Echo), MP_ROM_PTR(&audiodelays_echo_type) }, { MP_ROM_QSTR(MP_QSTR_Chorus), MP_ROM_PTR(&audiodelays_chorus_type) }, + { MP_ROM_QSTR(MP_QSTR_Flanger), MP_ROM_PTR(&audiodelays_flanger_type) }, { MP_ROM_QSTR(MP_QSTR_PitchShift), MP_ROM_PTR(&audiodelays_pitch_shift_type) }, { MP_ROM_QSTR(MP_QSTR_GranularPitchShift), MP_ROM_PTR(&audiodelays_granular_pitch_shift_type) }, { MP_ROM_QSTR(MP_QSTR_MultiTapDelay), MP_ROM_PTR(&audiodelays_multi_tap_delay_type) }, diff --git a/shared-bindings/audiofreeverb/Freeverb.c b/shared-bindings/audiofreeverb/Freeverb.c index 12eb7ef70ab..83c9eb7c01f 100644 --- a/shared-bindings/audiofreeverb/Freeverb.c +++ b/shared-bindings/audiofreeverb/Freeverb.c @@ -24,6 +24,8 @@ //| self, //| roomsize: synthio.BlockInput = 0.5, //| damp: synthio.BlockInput = 0.5, +//| pre_filter: Optional[synthio.Biquad | Tuple[synthio.Biquad]] = None, +//| post_filter: Optional[synthio.Biquad | Tuple[synthio.Biquad]] = None, //| mix: synthio.BlockInput = 0.5, //| buffer_size: int = 512, //| sample_rate: int = 8000, @@ -40,6 +42,8 @@ //| //| :param synthio.BlockInput roomsize: The size of the room. 0.0 = smallest; 1.0 = largest. //| :param synthio.BlockInput damp: How much the walls absorb. 0.0 = least; 1.0 = most. +//| :param Optional[synthio.Biquad|Tuple[synthio.Biquad]] pre_filter: A normalized biquad filter object or tuple of normalized biquad filter objects. The sample is processed sequentially by each "Pre-EQ" filter before being fed to the reverb effect. +//| :param Optional[synthio.Biquad|Tuple[synthio.Biquad]] post_filter: A normalized biquad filter object or tuple of normalized biquad filter objects. The output of the reverb effect is processed sequentially by each "Post-EQ" filter before being mixed with the original sample. //| :param synthio.BlockInput mix: The mix as a ratio of the sample (0.0) to the effect (1.0). //| :param int buffer_size: The total size in bytes of each of the two playback buffers to use //| :param int sample_rate: The sample rate to be used @@ -70,10 +74,12 @@ //| ... //| static mp_obj_t audiofreeverb_freeverb_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - enum { ARG_roomsize, ARG_damp, ARG_mix, ARG_buffer_size, ARG_sample_rate, ARG_bits_per_sample, ARG_samples_signed, ARG_channel_count, }; + enum { ARG_roomsize, ARG_damp, ARG_pre_filter, ARG_post_filter, ARG_mix, ARG_buffer_size, ARG_sample_rate, ARG_bits_per_sample, ARG_samples_signed, ARG_channel_count, }; static const mp_arg_t allowed_args[] = { { MP_QSTR_roomsize, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_damp, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_pre_filter, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } }, + { MP_QSTR_post_filter, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } }, { MP_QSTR_mix, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_buffer_size, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 512} }, { MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 8000} }, @@ -96,7 +102,7 @@ static mp_obj_t audiofreeverb_freeverb_make_new(const mp_obj_type_t *type, size_ } audiofreeverb_freeverb_obj_t *self = mp_obj_malloc(audiofreeverb_freeverb_obj_t, &audiofreeverb_freeverb_type); - common_hal_audiofreeverb_freeverb_construct(self, args[ARG_roomsize].u_obj, args[ARG_damp].u_obj, args[ARG_mix].u_obj, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate); + common_hal_audiofreeverb_freeverb_construct(self, args[ARG_roomsize].u_obj, args[ARG_damp].u_obj, args[ARG_pre_filter].u_obj, args[ARG_post_filter].u_obj, args[ARG_mix].u_obj, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate); return MP_OBJ_FROM_PTR(self); } @@ -165,6 +171,51 @@ MP_PROPERTY_GETSET(audiofreeverb_freeverb_damp_obj, (mp_obj_t)&audiofreeverb_freeverb_get_damp_obj, (mp_obj_t)&audiofreeverb_freeverb_set_damp_obj); + +//| pre_filter: synthio.Biquad | Tuple[synthio.Biquad] | None +//| """A normalized biquad filter object or tuple of normalized biquad filter objects. The sample is processed sequentially by each "Pre-EQ" filter before being fed to the reverb effect.""" +//| +static mp_obj_t audiofreeverb_freeverb_obj_get_pre_filter(mp_obj_t self_in) { + audiofreeverb_freeverb_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return common_hal_audiofreeverb_freeverb_get_pre_filter(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiofreeverb_freeverb_get_pre_filter_obj, audiofreeverb_freeverb_obj_get_pre_filter); + +static mp_obj_t audiofreeverb_freeverb_obj_set_pre_filter(mp_obj_t self_in, mp_obj_t filter_in) { + audiofreeverb_freeverb_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiofreeverb_freeverb_set_pre_filter(self, filter_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiofreeverb_freeverb_set_pre_filter_obj, audiofreeverb_freeverb_obj_set_pre_filter); + +MP_PROPERTY_GETSET(audiofreeverb_freeverb_pre_filter_obj, + (mp_obj_t)&audiofreeverb_freeverb_get_pre_filter_obj, + (mp_obj_t)&audiofreeverb_freeverb_set_pre_filter_obj); + + +//| post_filter: synthio.Biquad | Tuple[synthio.Biquad] | None +//| """A normalized biquad filter object or tuple of normalized biquad filter objects. The output of the reverb effect is processed sequentially by each "Post-EQ" filter before being mixed with the original sample.""" +//| +static mp_obj_t audiofreeverb_freeverb_obj_get_post_filter(mp_obj_t self_in) { + audiofreeverb_freeverb_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return common_hal_audiofreeverb_freeverb_get_post_filter(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiofreeverb_freeverb_get_post_filter_obj, audiofreeverb_freeverb_obj_get_post_filter); + +static mp_obj_t audiofreeverb_freeverb_obj_set_post_filter(mp_obj_t self_in, mp_obj_t filter_in) { + audiofreeverb_freeverb_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiofreeverb_freeverb_set_post_filter(self, filter_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiofreeverb_freeverb_set_post_filter_obj, audiofreeverb_freeverb_obj_set_post_filter); + +MP_PROPERTY_GETSET(audiofreeverb_freeverb_post_filter_obj, + (mp_obj_t)&audiofreeverb_freeverb_get_post_filter_obj, + (mp_obj_t)&audiofreeverb_freeverb_set_post_filter_obj); + + //| mix: synthio.BlockInput //| """The rate the reverb mix between 0 and 1 where 0 is only sample and 1 is all effect.""" static mp_obj_t audiofreeverb_freeverb_obj_get_mix(mp_obj_t self_in) { @@ -251,6 +302,8 @@ static const mp_rom_map_elem_t audiofreeverb_freeverb_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiofreeverb_freeverb_playing_obj) }, { MP_ROM_QSTR(MP_QSTR_roomsize), MP_ROM_PTR(&audiofreeverb_freeverb_roomsize_obj) }, { MP_ROM_QSTR(MP_QSTR_damp), MP_ROM_PTR(&audiofreeverb_freeverb_damp_obj) }, + { MP_ROM_QSTR(MP_QSTR_pre_filter), MP_ROM_PTR(&audiofreeverb_freeverb_pre_filter_obj) }, + { MP_ROM_QSTR(MP_QSTR_post_filter), MP_ROM_PTR(&audiofreeverb_freeverb_post_filter_obj) }, { MP_ROM_QSTR(MP_QSTR_mix), MP_ROM_PTR(&audiofreeverb_freeverb_mix_obj) }, AUDIOSAMPLE_FIELDS, }; diff --git a/shared-bindings/audiofreeverb/Freeverb.h b/shared-bindings/audiofreeverb/Freeverb.h index bde6cfffda7..7eed9775006 100644 --- a/shared-bindings/audiofreeverb/Freeverb.h +++ b/shared-bindings/audiofreeverb/Freeverb.h @@ -11,7 +11,7 @@ extern const mp_obj_type_t audiofreeverb_freeverb_type; void common_hal_audiofreeverb_freeverb_construct(audiofreeverb_freeverb_obj_t *self, - mp_obj_t roomsize, mp_obj_t damp, mp_obj_t mix, + mp_obj_t roomsize, mp_obj_t damp, mp_obj_t pre_filter, mp_obj_t post_filter, mp_obj_t mix, uint32_t buffer_size, uint8_t bits_per_sample, bool samples_signed, uint8_t channel_count, uint32_t sample_rate); @@ -24,6 +24,12 @@ void common_hal_audiofreeverb_freeverb_set_roomsize(audiofreeverb_freeverb_obj_t mp_obj_t common_hal_audiofreeverb_freeverb_get_damp(audiofreeverb_freeverb_obj_t *self); void common_hal_audiofreeverb_freeverb_set_damp(audiofreeverb_freeverb_obj_t *self, mp_obj_t damp); +mp_obj_t common_hal_audiofreeverb_freeverb_get_pre_filter(audiofreeverb_freeverb_obj_t *self); +void common_hal_audiofreeverb_freeverb_set_pre_filter(audiofreeverb_freeverb_obj_t *self, mp_obj_t arg); + +mp_obj_t common_hal_audiofreeverb_freeverb_get_post_filter(audiofreeverb_freeverb_obj_t *self); +void common_hal_audiofreeverb_freeverb_set_post_filter(audiofreeverb_freeverb_obj_t *self, mp_obj_t arg); + mp_obj_t common_hal_audiofreeverb_freeverb_get_mix(audiofreeverb_freeverb_obj_t *self); void common_hal_audiofreeverb_freeverb_set_mix(audiofreeverb_freeverb_obj_t *self, mp_obj_t mix); diff --git a/shared-bindings/audioi2sin/I2SIn.c b/shared-bindings/audioi2sin/I2SIn.c index ad369574c68..22f1fb2aaad 100644 --- a/shared-bindings/audioi2sin/I2SIn.c +++ b/shared-bindings/audioi2sin/I2SIn.c @@ -35,7 +35,6 @@ //| left_justified: bool = False, //| samples_signed: bool = True, //| external_clock: bool = False, -//| invert_bit_clock: bool = False, //| ) -> None: //| """Create an I2SIn object associated with the given pins. This allows you to //| record audio signals from an external I2S source (e.g. an I2S MEMS microphone @@ -95,9 +94,6 @@ //| declaration rather than a measurement: the real rate is whatever the external word select //| runs at, and `sample_rate` still reports the declared value. If the incoming clock stops, //| `record` blocks (interruptible with Ctrl-C). -//| :param bool invert_bit_clock: Sample ``data`` on the falling edge of ``bit_clock`` instead of -//| the rising edge. Needed when the external clock source drives its data on the rising edge. -//| Only valid together with ``external_clock``. //| //| Example, recording 16-bit mono samples from an INMP441:: //| @@ -121,7 +117,7 @@ static mp_obj_t audioi2sin_i2sin_make_new(const mp_obj_type_t *type, size_t n_ar enum { ARG_bit_clock, ARG_word_select, ARG_data, ARG_main_clock, ARG_sample_rate, ARG_bit_depth, ARG_output_bit_depth, ARG_mono, ARG_left_justified, ARG_samples_signed, - ARG_external_clock, ARG_invert_bit_clock }; + ARG_external_clock }; static const mp_arg_t allowed_args[] = { { MP_QSTR_bit_clock, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_word_select, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -134,17 +130,11 @@ static mp_obj_t audioi2sin_i2sin_make_new(const mp_obj_type_t *type, size_t n_ar { MP_QSTR_left_justified, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, { MP_QSTR_samples_signed, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, { MP_QSTR_external_clock, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, - { MP_QSTR_invert_bit_clock, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); bool external_clock = args[ARG_external_clock].u_bool; - bool invert_bit_clock = args[ARG_invert_bit_clock].u_bool; - if (invert_bit_clock && !external_clock) { - mp_raise_ValueError_varg(MP_ERROR_TEXT("%q requires %q"), - MP_QSTR_invert_bit_clock, MP_QSTR_external_clock); - } // In external clock mode the clock pins are only read, so they may already // be owned by whatever is driving them; let the port decide if the sharing @@ -181,7 +171,7 @@ static mp_obj_t audioi2sin_i2sin_make_new(const mp_obj_type_t *type, size_t n_ar audioi2sin_i2sin_obj_t *self = mp_obj_malloc_with_finaliser(audioi2sin_i2sin_obj_t, &audioi2sin_i2sin_type); common_hal_audioi2sin_i2sin_construct(self, bit_clock, word_select, data, main_clock, sample_rate, bit_depth, output_bit_depth, mono, left_justified, samples_signed, - external_clock, invert_bit_clock); + external_clock); return MP_OBJ_FROM_PTR(self); #endif diff --git a/shared-bindings/audioi2sin/I2SIn.h b/shared-bindings/audioi2sin/I2SIn.h index 7877653858f..25f5489015d 100644 --- a/shared-bindings/audioi2sin/I2SIn.h +++ b/shared-bindings/audioi2sin/I2SIn.h @@ -21,7 +21,7 @@ void common_hal_audioi2sin_i2sin_construct(audioi2sin_i2sin_obj_t *self, const mcu_pin_obj_t *data, const mcu_pin_obj_t *main_clock, uint32_t sample_rate, uint8_t bit_depth, uint8_t output_bit_depth, bool mono, bool left_justified, bool samples_signed, - bool external_clock, bool invert_bit_clock); + bool external_clock); void common_hal_audioi2sin_i2sin_deinit(audioi2sin_i2sin_obj_t *self); bool common_hal_audioi2sin_i2sin_deinited(audioi2sin_i2sin_obj_t *self); uint32_t common_hal_audioi2sin_i2sin_record_to_buffer(audioi2sin_i2sin_obj_t *self, diff --git a/shared-bindings/audiomixer/MixerVoice.h b/shared-bindings/audiomixer/MixerVoice.h index d60820f7f57..89c87218e7c 100644 --- a/shared-bindings/audiomixer/MixerVoice.h +++ b/shared-bindings/audiomixer/MixerVoice.h @@ -14,6 +14,7 @@ extern const mp_obj_type_t audiomixer_mixervoice_type; void common_hal_audiomixer_mixervoice_construct(audiomixer_mixervoice_obj_t *self); void common_hal_audiomixer_mixervoice_set_parent(audiomixer_mixervoice_obj_t *self, audiomixer_mixer_obj_t *parent); void common_hal_audiomixer_mixervoice_play(audiomixer_mixervoice_obj_t *self, mp_obj_t sample, bool loop); +void common_hal_audiomixer_mixervoice_reset_buffer(audiomixer_mixervoice_obj_t *self); void common_hal_audiomixer_mixervoice_stop(audiomixer_mixervoice_obj_t *self); void common_hal_audiomixer_mixervoice_end(audiomixer_mixervoice_obj_t *self); mp_obj_t common_hal_audiomixer_mixervoice_get_level(audiomixer_mixervoice_obj_t *self); diff --git a/shared-bindings/mdns/RemoteService.c b/shared-bindings/mdns/RemoteService.c index b3ee3d7f7b7..b00313729fc 100644 --- a/shared-bindings/mdns/RemoteService.c +++ b/shared-bindings/mdns/RemoteService.c @@ -93,18 +93,6 @@ MP_DEFINE_CONST_FUN_OBJ_1(mdns_remoteservice_get_ipv4_address_obj, _mdns_remotes MP_PROPERTY_GETTER(mdns_remoteservice_ipv4_address_obj, (mp_obj_t)&mdns_remoteservice_get_ipv4_address_obj); -//| def __del__(self) -> None: -//| """Deletes the RemoteService object.""" -//| ... -//| -//| -static mp_obj_t mdns_remoteservice_obj_deinit(mp_obj_t self_in) { - mdns_remoteservice_obj_t *self = MP_OBJ_TO_PTR(self_in); - common_hal_mdns_remoteservice_deinit(self); - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_1(mdns_remoteservice_deinit_obj, mdns_remoteservice_obj_deinit); - static const mp_rom_map_elem_t mdns_remoteservice_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_hostname), MP_ROM_PTR(&mdns_remoteservice_hostname_obj) }, { MP_ROM_QSTR(MP_QSTR_instance_name), MP_ROM_PTR(&mdns_remoteservice_instance_name_obj) }, @@ -112,8 +100,6 @@ static const mp_rom_map_elem_t mdns_remoteservice_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_protocol), MP_ROM_PTR(&mdns_remoteservice_protocol_obj) }, { MP_ROM_QSTR(MP_QSTR_port), MP_ROM_PTR(&mdns_remoteservice_port_obj) }, { MP_ROM_QSTR(MP_QSTR_ipv4_address), MP_ROM_PTR(&mdns_remoteservice_ipv4_address_obj) }, - - { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mdns_remoteservice_deinit_obj) }, }; static MP_DEFINE_CONST_DICT(mdns_remoteservice_locals_dict, mdns_remoteservice_locals_dict_table); diff --git a/shared-bindings/mdns/RemoteService.h b/shared-bindings/mdns/RemoteService.h index a5ffe897e4e..c7efa64083d 100644 --- a/shared-bindings/mdns/RemoteService.h +++ b/shared-bindings/mdns/RemoteService.h @@ -19,7 +19,6 @@ const char *common_hal_mdns_remoteservice_get_instance_name(mdns_remoteservice_o const char *common_hal_mdns_remoteservice_get_hostname(mdns_remoteservice_obj_t *self); mp_int_t common_hal_mdns_remoteservice_get_port(mdns_remoteservice_obj_t *self); mp_obj_t common_hal_mdns_remoteservice_get_ipv4_address(mdns_remoteservice_obj_t *self); -void common_hal_mdns_remoteservice_deinit(mdns_remoteservice_obj_t *self); // For internal use. uint32_t mdns_remoteservice_get_ipv4_address(mdns_remoteservice_obj_t *self); diff --git a/shared-bindings/mdns/Server.c b/shared-bindings/mdns/Server.c index 86fb2eb6d7a..1c16348d425 100644 --- a/shared-bindings/mdns/Server.c +++ b/shared-bindings/mdns/Server.c @@ -150,7 +150,14 @@ static mp_obj_t _mdns_server_find(mp_uint_t n_args, const mp_obj_t *pos_args, mp } static MP_DEFINE_CONST_FUN_OBJ_KW(mdns_server_find_obj, 1, _mdns_server_find); -//| def advertise_service(self, *, service_type: str, protocol: str, port: int) -> None: +//| def advertise_service( +//| self, +//| *, +//| service_type: str, +//| protocol: str, +//| port: int, +//| txt_records: Optional[Sequence[str]] = None, +//| ) -> None: //| """Respond to queries for the given service with the given port. //| //| ``service_type`` and ``protocol`` can only occur on one port. Any call after the first @@ -158,8 +165,8 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(mdns_server_find_obj, 1, _mdns_server_find); //| //| If web workflow is active, the port it uses can't also be used to advertise a service. //| -//| **Limitations**: Publishing up to 32 TXT records is only supported on the RP2040 Pico W board at -//| this time. +//| **Limitations**: Publishing TXT records (up to 32) is supported only on RP2xxx. +//| There is currently no TXT record support on Espressif boards. //| //| :param str service_type: The service type such as "_http" //| :param str protocol: The service protocol such as "_tcp" diff --git a/shared-bindings/picogame/Bitmap.c b/shared-bindings/picogame/Bitmap.c new file mode 100644 index 00000000000..8c970a5a0c0 --- /dev/null +++ b/shared-bindings/picogame/Bitmap.c @@ -0,0 +1,242 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT +// +// picogame.Bitmap: pixel data (PAL8 or RGB565) shared by sprites, tilemaps and blits. + +#include "py/runtime.h" +#include "shared-module/picogame/pg_compat.h" +#include "shared-bindings/busdisplay/BusDisplay.h" +#include "shared-bindings/picogame/__init__.h" +#include "shared-bindings/picogame/Bitmap.h" +#include "shared-bindings/picogame/Sprite.h" +#if CIRCUITPY_PICOGAME_FAST_DISPLAY +#include "shared-bindings/picogame/Display.h" // fast DMA backend; absent on portable ports +#include "common-hal/picogame/Display.h" // its struct (pg_get_display unwraps the wrapper) +#endif +#include "shared-bindings/picogame/Scene.h" +#include "shared-bindings/picogame/Tilemap.h" +#include "shared-bindings/picogame/Particles.h" +#include "shared-bindings/picogame/Canvas.h" +#include "shared-bindings/picogame/Framebuffer.h" +#include "shared-module/picogame/__init__.h" +#include "shared-module/picogame/Bitmap.h" +#include "shared-module/picogame/Sprite.h" + +// --------------------------------------------------------------------------- +// Bitmap +// --------------------------------------------------------------------------- + +//| class Bitmap: +//| """An image atlas of one or more frames that all share one size. RGB565 +//| pixel data and palette entries must be in the display's transfer byte +//| order (use :py:func:`rgb565` to build colors); PAL8 pixel data are +//| palette indices. +//| +//| The bitmap references the caller-provided data without copying or modifying +//| it. The backing buffer may live in RAM or in read-only memory.""" +//| +//| def __init__( +//| self, +//| data: ReadableBuffer, +//| width: int, +//| height: int, +//| *, +//| format: int = RGB565, +//| palette: Optional[ReadableBuffer] = None, +//| frames: int = 1, +//| stride: int = 0, +//| transparent: Optional[int] = None, +//| ) -> None: +//| """:param ~circuitpython_typing.ReadableBuffer data: RGB565 pixel data in +//| the display's transfer byte order, or palette indices for ``PAL8`` +//| :param int width: width of one frame in pixels +//| :param int height: height of one frame in pixels +//| :param int format: :py:data:`RGB565` (2 bytes per pixel) or :py:data:`PAL8` +//| (1 byte per pixel, indexing ``palette``) +//| :param ~circuitpython_typing.ReadableBuffer palette: for ``PAL8``, a buffer +//| of transfer-order RGB565 colors, two bytes per entry. Every byte in +//| ``data`` must be a valid palette index, that is, less than +//| ``len(palette) // 2``. +//| :param int frames: number of equal-size frames, laid out left to right in +//| one horizontal atlas +//| :param int stride: distance between two rows in pixels. ``0``, the +//| default, means ``width * frames``; set it explicitly to reference a +//| sub-region of a wider image. +//| :param int transparent: color (``RGB565``) or index (``PAL8``) that is +//| skipped when drawing. Defaults to `None`, fully opaque.""" +//| ... +//| +static mp_obj_t picogame_bitmap_make_new(const mp_obj_type_t *type, size_t n_args, + size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_data, ARG_width, ARG_height, ARG_format, ARG_palette, ARG_frames, ARG_stride, ARG_transparent }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_width, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_height, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_format, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = PICOGAME_FMT_RGB565} }, + { MP_QSTR_palette, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_frames, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, + { MP_QSTR_stride, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_transparent, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // bound BEFORE any arithmetic: an unbounded width could overflow width*frames (int32) and wrap + // small, slipping past the size guards below into an undersized buffer (OOB read in the blitter). + mp_int_t width = mp_arg_validate_int_range(args[ARG_width].u_int, 1, 65535, MP_QSTR_width); + mp_int_t height = mp_arg_validate_int_range(args[ARG_height].u_int, 1, 65535, MP_QSTR_height); + mp_int_t frames = mp_arg_validate_int_range(args[ARG_frames].u_int, 1, 255, MP_QSTR_frames); + mp_int_t format = args[ARG_format].u_int; + if (format != PICOGAME_FMT_RGB565 && format != PICOGAME_FMT_PAL8) { + mp_raise_ValueError(MP_ERROR_TEXT("Invalid format")); + } + mp_int_t stride = args[ARG_stride].u_int; + if (stride <= 0) { + stride = width * frames; + } + // stride must hold the whole horizontal atlas, and the dims must fit the uint16_t fields, + // or blits index past a row / the stored stride truncates. + mp_arg_validate_int_max(width * frames, 65535, MP_QSTR_width); + mp_arg_validate_int_max(stride, 65535, MP_QSTR_stride); + mp_arg_validate_int_min(stride, width * frames, MP_QSTR_stride); + + mp_buffer_info_t data_info; + mp_get_buffer_raise(args[ARG_data].u_obj, &data_info, MP_BUFFER_READ); + + const uint16_t *palette = NULL; + mp_obj_t palette_obj = MP_OBJ_NULL; + size_t pal_len = 0; + if (format == PICOGAME_FMT_PAL8) { + if (args[ARG_palette].u_obj == mp_const_none) { + mp_arg_error_invalid(MP_QSTR_palette); // PAL8 needs one + } + mp_buffer_info_t pal_info; + mp_get_buffer_raise(args[ARG_palette].u_obj, &pal_info, MP_BUFFER_READ); + palette = pal_info.buf; + palette_obj = args[ARG_palette].u_obj; + pal_len = pal_info.len; + } + + size_t bpp = (format == PICOGAME_FMT_PAL8) ? 1 : 2; + // 64-bit: stride*height*bpp can exceed 32 bits (stride,height <= 65535) and wrap small in a 32-bit + // size_t, letting a tiny buffer pass this check -> OOB read in the blitter. Compute + compare wide. + uint64_t need = (uint64_t)stride * (uint64_t)height * (uint64_t)bpp; + if ((uint64_t)data_info.len < need) { + mp_raise_ValueError(MP_ERROR_TEXT("buffer too small")); + } + if (format == PICOGAME_FMT_PAL8 && pal_len < 2) { + // Need >=1 entry so pal[0] is valid. Contract (see blitter): PAL8 indices MUST be < palette + // length. The caller may write indices into the (mutable) `data` buffer directly, so we cannot + // validate once here. An out-of-range index is undefined behaviour (reads past the palette: + // usually a garbage colour, but it may fault on some platforms). Per-pixel clamping was dropped + // for speed - see the "restore full bounds-safety" note in the PAL8 blit loop if it's needed. + mp_arg_validate_length_min(pal_len, 1, MP_QSTR_palette); + } + + picogame_bitmap_obj_t *self = mp_obj_malloc(picogame_bitmap_obj_t, type); + self->data_obj = args[ARG_data].u_obj; + self->palette_obj = palette_obj; + self->data = data_info.buf; + self->palette = palette; + self->width = width; + self->height = height; + self->stride = stride; + self->frames = frames; + self->format = format; + // palette length in entries (informational; blitter assumes indices < this - see blit contract). + self->pal_entries = (uint16_t)((pal_len / 2) > 65535 ? 65535 : (pal_len / 2)); + if (args[ARG_transparent].u_obj != mp_const_none) { + self->transparent = mp_obj_get_int(args[ARG_transparent].u_obj); + self->has_transparent = true; + } else { + self->transparent = 0; + self->has_transparent = false; + } + return MP_OBJ_FROM_PTR(self); +} + +//| +//| width: int +//| """Width of one frame in pixels. (read-only)""" +//| height: int +//| """Height of one frame in pixels. (read-only)""" +//| frames: int +//| """Number of frames in the atlas. (read-only)""" +static mp_obj_t bitmap_get_width(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(((picogame_bitmap_obj_t *)MP_OBJ_TO_PTR(self_in))->width); +} +static MP_DEFINE_CONST_FUN_OBJ_1(bitmap_get_width_obj, bitmap_get_width); +MP_PROPERTY_GETTER(bitmap_width_obj, (mp_obj_t)&bitmap_get_width_obj); + +static mp_obj_t bitmap_get_height(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(((picogame_bitmap_obj_t *)MP_OBJ_TO_PTR(self_in))->height); +} +static MP_DEFINE_CONST_FUN_OBJ_1(bitmap_get_height_obj, bitmap_get_height); +MP_PROPERTY_GETTER(bitmap_height_obj, (mp_obj_t)&bitmap_get_height_obj); + +static mp_obj_t bitmap_get_frames(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(((picogame_bitmap_obj_t *)MP_OBJ_TO_PTR(self_in))->frames); +} +static MP_DEFINE_CONST_FUN_OBJ_1(bitmap_get_frames_obj, bitmap_get_frames); +MP_PROPERTY_GETTER(bitmap_frames_obj, (mp_obj_t)&bitmap_get_frames_obj); + +//| format: int +//| """:py:data:`RGB565` or :py:data:`PAL8`. (read-only)""" +static mp_obj_t bitmap_get_format(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(((picogame_bitmap_obj_t *)MP_OBJ_TO_PTR(self_in))->format); +} +static MP_DEFINE_CONST_FUN_OBJ_1(bitmap_get_format_obj, bitmap_get_format); +MP_PROPERTY_GETTER(bitmap_format_obj, (mp_obj_t)&bitmap_get_format_obj); + +//| stride: int +//| """Row stride in pixels. (read-only)""" +static mp_obj_t bitmap_get_stride(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(((picogame_bitmap_obj_t *)MP_OBJ_TO_PTR(self_in))->stride); +} +static MP_DEFINE_CONST_FUN_OBJ_1(bitmap_get_stride_obj, bitmap_get_stride); +MP_PROPERTY_GETTER(bitmap_stride_obj, (mp_obj_t)&bitmap_get_stride_obj); + +//| palette: Optional[ReadableBuffer] +//| """The ``PAL8`` palette buffer this Bitmap was built with, or `None` for +//| ``RGB565``. (read-only)""" +static mp_obj_t bitmap_get_palette(mp_obj_t self_in) { + picogame_bitmap_obj_t *self = MP_OBJ_TO_PTR(self_in); + return (self->palette_obj == MP_OBJ_NULL) ? mp_const_none : self->palette_obj; +} +static MP_DEFINE_CONST_FUN_OBJ_1(bitmap_get_palette_obj, bitmap_get_palette); +MP_PROPERTY_GETTER(bitmap_palette_obj, (mp_obj_t)&bitmap_get_palette_obj); + +//| transparent: Optional[int] +//| """The transparent color or index, or `None` if the Bitmap is fully +//| opaque. (read-only)""" +//| +//| +static mp_obj_t bitmap_get_transparent(mp_obj_t self_in) { + picogame_bitmap_obj_t *self = MP_OBJ_TO_PTR(self_in); + return self->has_transparent ? MP_OBJ_NEW_SMALL_INT(self->transparent) : mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(bitmap_get_transparent_obj, bitmap_get_transparent); +MP_PROPERTY_GETTER(bitmap_transparent_obj, (mp_obj_t)&bitmap_get_transparent_obj); + +static const mp_rom_map_elem_t picogame_bitmap_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&bitmap_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&bitmap_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_frames), MP_ROM_PTR(&bitmap_frames_obj) }, + { MP_ROM_QSTR(MP_QSTR_format), MP_ROM_PTR(&bitmap_format_obj) }, + { MP_ROM_QSTR(MP_QSTR_stride), MP_ROM_PTR(&bitmap_stride_obj) }, + { MP_ROM_QSTR(MP_QSTR_palette), MP_ROM_PTR(&bitmap_palette_obj) }, + { MP_ROM_QSTR(MP_QSTR_transparent), MP_ROM_PTR(&bitmap_transparent_obj) }, +}; +static MP_DEFINE_CONST_DICT(picogame_bitmap_locals_dict, picogame_bitmap_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + picogame_bitmap_type, + MP_QSTR_Bitmap, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, picogame_bitmap_make_new, + locals_dict, &picogame_bitmap_locals_dict + ); diff --git a/shared-bindings/picogame/Bitmap.h b/shared-bindings/picogame/Bitmap.h new file mode 100644 index 00000000000..44e2a2fb1aa --- /dev/null +++ b/shared-bindings/picogame/Bitmap.h @@ -0,0 +1,11 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-module/picogame/Bitmap.h" + +extern const mp_obj_type_t picogame_bitmap_type; diff --git a/shared-bindings/picogame/Canvas.c b/shared-bindings/picogame/Canvas.c new file mode 100644 index 00000000000..675826936ff --- /dev/null +++ b/shared-bindings/picogame/Canvas.c @@ -0,0 +1,623 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#include "py/runtime.h" +#include "shared-module/picogame/pg_compat.h" +#include "shared-bindings/picogame/Canvas.h" +#include "shared-bindings/picogame/Bitmap.h" +#include "shared-bindings/fontio/BuiltinFont.h" +#include "shared-module/picogame/Canvas.h" + +//| class Canvas: +//| """A RAM drawing surface composited as a `Scene` layer. Draw primitives +//| into it; only redrawn areas repaint. Colors come from +//| :py:func:`rgb565`.""" +//| +//| def __init__( +//| self, +//| width: int, +//| height: int, +//| *, +//| transparent: Optional[int] = None, +//| buffer: Optional[WriteableBuffer] = None, +//| ) -> None: +//| """:param int width: surface width in pixels +//| :param int height: surface height in pixels +//| :param int transparent: color that is skipped when the canvas is +//| composited; `None` makes every pixel opaque +//| :param ~circuitpython_typing.WriteableBuffer buffer: optional caller-owned +//| pixel buffer of at least ``width * height * 2`` bytes (for example a +//| ``bytearray`` or a writable ``memoryview``). The canvas draws into it +//| instead of allocating its own.""" +//| ... +//| +static mp_obj_t picogame_canvas_make_new(const mp_obj_type_t *type, size_t n_args, + size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_width, ARG_height, ARG_transparent, ARG_buffer }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_width, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_height, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_transparent, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_buffer, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_int_t w = mp_arg_validate_int_range(args[ARG_width].u_int, 1, 1024, MP_QSTR_width); + mp_int_t h = mp_arg_validate_int_range(args[ARG_height].u_int, 1, 1024, MP_QSTR_height); + + picogame_canvas_obj_t *self = mp_obj_malloc(picogame_canvas_obj_t, type); + self->w = w; + self->h = h; + self->x = 0; + self->y = 0; + if (args[ARG_buffer].u_obj != mp_const_none) { + // external buffer (e.g. an arena slice) - draw into it, don't allocate/own it + mp_buffer_info_t bi; + mp_get_buffer_raise(args[ARG_buffer].u_obj, &bi, MP_BUFFER_RW); + if (bi.len < (size_t)w * h * 2) { + mp_raise_ValueError(MP_ERROR_TEXT("buffer too small")); + } + if ((uintptr_t)bi.buf & 1) { // odd byte address -> uint16 pixel stores fault on Cortex-M0+ + mp_raise_ValueError_varg(MP_ERROR_TEXT("Buffer must be a multiple of %d bytes"), 2); + } + self->data = bi.buf; + self->data_obj = args[ARG_buffer].u_obj; // keep the backing object alive (GC-traced) + } else { + // Pure pixel data, no Python pointers -> exempt from the conservative GC scan + // (shorter gc.collect() pauses; a 150x60 canvas is 18 KB the mark phase can skip). + self->data = m_malloc_without_collect((size_t)w * h * sizeof(uint16_t)); + self->data_obj = MP_OBJ_NULL; + } + if (args[ARG_transparent].u_obj != mp_const_none) { + self->transparent = mp_obj_get_int(args[ARG_transparent].u_obj); + self->has_transparent = true; + } else { + self->transparent = 0; + self->has_transparent = false; + } + uint16_t fill = self->has_transparent ? self->transparent : 0; + for (size_t i = 0; i < (size_t)w * h; i++) { + self->data[i] = fill; + } + picogame_canvas_dirty_reset(self); + return MP_OBJ_FROM_PTR(self); +} + +static picogame_canvas_obj_t *cv_self(mp_obj_t o) { + return MP_OBJ_TO_PTR(o); +} + +// Shared int-arg unpacker for the plain drawing trampolines below: every one of them is +// "self + N ints -> void", and the inlined per-wrapper mp_obj_get_int runs cost ~70-120 B +// each at -Os. One loop here + a tiny per-wrapper stub keeps the flash cost per method at +// ~2 calls. n comes from the VAR_BETWEEN exact arity, so v[] is always fully written. +static picogame_canvas_obj_t *cv_args(const mp_obj_t *a, size_t n, int *v) { + for (size_t i = 1; i < n; i++) { + v[i - 1] = mp_obj_get_int(a[i]); + } + return cv_self(a[0]); +} + +//| def clear(self, color: int) -> None: +//| """Fill the whole surface with ``color``.""" +//| ... +//| +static mp_obj_t canvas_clear(mp_obj_t self_in, mp_obj_t color) { + picogame_canvas_clear(cv_self(self_in), mp_obj_get_int(color)); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(canvas_clear_obj, canvas_clear); + +//| +//| def pixel(self, x: int, y: int, color: int) -> None: +//| """Set a single pixel.""" +//| ... +//| +static mp_obj_t canvas_pixel(size_t n, const mp_obj_t *a) { + int v[3]; + picogame_canvas_pixel(cv_args(a, n, v), v[0], v[1], v[2]); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canvas_pixel_obj, 4, 4, canvas_pixel); + +//| +//| def fill_rect(self, x: int, y: int, w: int, h: int, color: int) -> None: +//| """Fill an axis-aligned rectangle.""" +//| ... +//| +static mp_obj_t canvas_fill_rect(size_t n, const mp_obj_t *a) { + int v[5]; + picogame_canvas_fill_rect(cv_args(a, n, v), v[0], v[1], v[2], v[3], v[4]); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canvas_fill_rect_obj, 6, 6, canvas_fill_rect); + +//| +//| def blit( +//| self, +//| bitmap: Bitmap, +//| x: int, +//| y: int, +//| frame: int = 0, +//| flip_x: bool = False, +//| flip_y: bool = False, +//| ) -> None: +//| """Copy one frame of ``bitmap``, selected by ``frame``, into the canvas +//| at ``(x, y)``, honoring the bitmap's transparent color.""" +//| ... +//| +static mp_obj_t canvas_blit(size_t n, const mp_obj_t *a) { + picogame_bitmap_obj_t *bm = MP_OBJ_TO_PTR( + mp_arg_validate_type(a[1], &picogame_bitmap_type, MP_QSTR_bitmap)); + // Same domain as Sprite.frame (uint8): a negative frame would survive the blitter's + // wrap (C % keeps the sign) and read before the sheet data. + int frame = (n > 4) ? (int)mp_arg_validate_int_range(mp_obj_get_int(a[4]), 0, 255, MP_QSTR_frame) : 0; + bool fx = (n > 5) ? mp_obj_is_true(a[5]) : false; + bool fy = (n > 6) ? mp_obj_is_true(a[6]) : false; + picogame_canvas_blit(cv_self(a[0]), bm, mp_obj_get_int(a[2]), mp_obj_get_int(a[3]), frame, fx, fy); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canvas_blit_obj, 4, 7, canvas_blit); + +//| def mode7( +//| self, +//| texture: Bitmap, +//| horizon: int, +//| y_off: int, +//| z: int, +//| rx0: int, +//| ry0: int, +//| rsx: int, +//| rsy: int, +//| cam_x: int, +//| cam_y: int, +//| ) -> None: +//| """Fill rows below ``horizon`` with a perspective projection of ``texture``. +//| The ``picogame_mode7`` helper computes the camera terms from an angle, +//| position and field of view. +//| +//| :param Bitmap texture: texture whose width and height are powers of two +//| :param int horizon: first canvas row to fill +//| :param int y_off: vertical offset of the projection, in rows +//| :param int z: camera height (signed 16.16 fixed-point) +//| :param int rx0: ray x at the left column (signed 16.16 fixed-point) +//| :param int ry0: ray y at the left column (signed 16.16 fixed-point) +//| :param int rsx: per-column ray step x (signed 16.16 fixed-point) +//| :param int rsy: per-column ray step y (signed 16.16 fixed-point) +//| :param int cam_x: camera x position (signed 16.16 fixed-point) +//| :param int cam_y: camera y position (signed 16.16 fixed-point)""" +//| ... +//| +static mp_obj_t canvas_mode7(size_t n, const mp_obj_t *a) { + picogame_bitmap_obj_t *tex = MP_OBJ_TO_PTR( + mp_arg_validate_type(a[1], &picogame_bitmap_type, MP_QSTR_texture)); + picogame_canvas_mode7(cv_self(a[0]), tex, + mp_obj_get_int(a[2]), mp_obj_get_int(a[3]), mp_obj_get_int(a[4]), + mp_obj_get_int(a[5]), mp_obj_get_int(a[6]), mp_obj_get_int(a[7]), + mp_obj_get_int(a[8]), mp_obj_get_int(a[9]), mp_obj_get_int(a[10])); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canvas_mode7_obj, 11, 11, canvas_mode7); + +//| def fill_triangles( +//| self, +//| verts: ReadableBuffer, +//| colors: ReadableBuffer, +//| n: int, +//| x_off: int = 0, +//| y_off: int = 0, +//| ) -> None: +//| """Fill a batch of triangles in one call, which is faster than repeated +//| :py:meth:`fill_triangle` calls for many triangles. +//| +//| :param ~circuitpython_typing.ReadableBuffer verts: ``6 * n`` ``int16`` +//| values: x0, y0, x1, y1, x2, y2 per triangle +//| :param ~circuitpython_typing.ReadableBuffer colors: ``n`` ``uint16`` colors, +//| one per triangle +//| :param int n: number of triangles +//| :param int x_off: added to every x before clipping +//| :param int y_off: added to every y before clipping +//| +//| The offsets translate the whole batch, so one screen-space batch can be +//| replayed into each `StripDraw` view by passing the negated view origin +//| (``x_off=-vx, y_off=-vy``); triangles outside the view are skipped.""" +//| ... +//| +static mp_obj_t canvas_fill_triangles(size_t na, const mp_obj_t *a) { + picogame_canvas_obj_t *cv = cv_self(a[0]); + mp_buffer_info_t vi, ci; + mp_get_buffer_raise(a[1], &vi, MP_BUFFER_READ); + mp_get_buffer_raise(a[2], &ci, MP_BUFFER_READ); + int n = mp_obj_get_int(a[3]); + int xo = na > 4 ? mp_obj_get_int(a[4]) : 0; + int yo = na > 5 ? mp_obj_get_int(a[5]) : 0; + const int16_t *v = vi.buf; + const uint16_t *col = ci.buf; + int cap_v = (int)(vi.len / 12); // 6 int16 = 12 bytes per triangle + int cap_c = (int)(ci.len >> 1); + if (n > cap_v) { + n = cap_v; + } + if (n > cap_c) { + n = cap_c; + } + picogame_fill_triangle_batch(cv, v, col, n, xo, yo); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canvas_fill_triangles_obj, 4, 6, canvas_fill_triangles); + +//| def vspans( +//| self, +//| x0s: ReadableBuffer, +//| x1s: ReadableBuffer, +//| tops: ReadableBuffer, +//| bots: ReadableBuffer, +//| colors: ReadableBuffer, +//| n: int, +//| x_off: int = 0, +//| y_off: int = 0, +//| ) -> None: +//| """Fill a batch of vertical color spans in one call. Span ``i`` covers +//| columns ``x0s[i]`` through ``x1s[i]`` (exclusive) and rows ``tops[i]`` +//| through ``bots[i]`` (exclusive) in color ``colors[i]``. +//| +//| :param ~circuitpython_typing.ReadableBuffer x0s: ``n`` ``uint16`` left edges +//| :param ~circuitpython_typing.ReadableBuffer x1s: ``n`` ``uint16`` exclusive +//| right edges +//| :param ~circuitpython_typing.ReadableBuffer tops: ``n`` ``uint16`` top rows +//| :param ~circuitpython_typing.ReadableBuffer bots: ``n`` ``uint16`` exclusive +//| bottom rows +//| :param ~circuitpython_typing.ReadableBuffer colors: ``n`` ``uint16`` colors +//| :param int n: number of spans +//| :param int x_off: added to every x before clipping +//| :param int y_off: added to every y before clipping +//| +//| The offsets translate the whole batch, so one screen-space batch (for +//| example `raycast` wall runs) can be replayed into each `StripDraw` view by +//| passing the negated view origin; spans outside the view are skipped.""" +//| ... +//| +static mp_obj_t canvas_vspans(size_t na, const mp_obj_t *a) { + picogame_canvas_obj_t *cv = cv_self(a[0]); + // five equal-shape uint16 arrays in a row: fetch + shortest-length fold in one loop + mp_buffer_info_t bi5[5]; + size_t cap = (size_t)-1; + for (int i = 0; i < 5; i++) { + mp_get_buffer_raise(a[1 + i], &bi5[i], MP_BUFFER_READ); + if (bi5[i].len < cap) { + cap = bi5[i].len; + } + } + int n = mp_obj_get_int(a[6]); + int xo = na > 7 ? mp_obj_get_int(a[7]) : 0; + int yo = na > 8 ? mp_obj_get_int(a[8]) : 0; + if (n > (int)(cap >> 1)) { + n = (int)(cap >> 1); // never read past the shortest array + } + const uint16_t *x0s = bi5[0].buf; + const uint16_t *x1s = bi5[1].buf; + const uint16_t *tops = bi5[2].buf; + const uint16_t *bots = bi5[3].buf; + const uint16_t *cols = bi5[4].buf; + int cw = cv->w, ch = cv->h; + for (int i = 0; i < n; i++) { + int t = tops[i] + yo, b = bots[i] + yo; + if (b <= 0 || t >= ch || b <= t) { + continue; // span outside this band + } + int x0 = x0s[i] + xo, x1 = x1s[i] + xo; + if (x1 <= 0 || x0 >= cw || x1 <= x0) { + continue; + } + picogame_canvas_fill_rect(cv, x0, t, x1 - x0, b - t, cols[i]); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canvas_vspans_obj, 7, 9, canvas_vspans); + +//| def road( +//| self, +//| ri0: int, +//| tab: ReadableBuffer, +//| rl: ReadableBuffer, +//| rr: ReadableBuffer, +//| d05_q8: int, +//| d07_q8: int, +//| colors: ReadableBuffer, +//| ) -> None: +//| """Draw one racing-road strip from precomputed tables. A low-level +//| interface used by the ``picogame_road`` helper, which builds the tables. +//| +//| :param int ri0: road-table row of this surface's row 0; negative values +//| are sky rows +//| :param ~circuitpython_typing.ReadableBuffer tab: ``int16`` rows of +//| ``edge_w``, ``dash_hw``, ``wb05_q8``, ``wb07_q8``, ``flags`` +//| :param ~circuitpython_typing.ReadableBuffer rl: ``int16`` per-row left +//| edges, as computed by :py:func:`road_edges` +//| :param ~circuitpython_typing.ReadableBuffer rr: ``int16`` per-row right +//| edges +//| :param int d05_q8: scroll phase (Q8 fixed-point) +//| :param int d07_q8: scroll phase (Q8 fixed-point) +//| :param ~circuitpython_typing.ReadableBuffer colors: six ``uint16`` colors, +//| in order: sky, road a, road b, rumble a, rumble b, dash""" +//| ... +//| +static mp_obj_t canvas_road(size_t n, const mp_obj_t *a) { + mp_buffer_info_t tabi, rli, rri, coli; + mp_get_buffer_raise(a[2], &tabi, MP_BUFFER_READ); + mp_get_buffer_raise(a[3], &rli, MP_BUFFER_READ); + mp_get_buffer_raise(a[4], &rri, MP_BUFFER_READ); + mp_get_buffer_raise(a[7], &coli, MP_BUFFER_READ); + int ntab = (int)(tabi.len / (5 * 2)); + int nedge = (int)(rli.len < rri.len ? rli.len : rri.len) / 2; + if (nedge < ntab) { + ntab = nedge; // never read past the shorter per-frame arrays + } + if (ntab <= 0 || coli.len < 6 * 2) { + return mp_const_none; + } + picogame_canvas_road(cv_self(a[0]), mp_obj_get_int(a[1]), + (const int16_t *)tabi.buf, ntab, (const int16_t *)rli.buf, (const int16_t *)rri.buf, + mp_obj_get_int(a[5]), mp_obj_get_int(a[6]), (const uint16_t *)coli.buf); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canvas_road_obj, 8, 8, canvas_road); + +//| def rect(self, x: int, y: int, w: int, h: int, color: int) -> None: +//| """Draw a one-pixel rectangle outline.""" +//| ... +//| +static mp_obj_t canvas_rect(size_t n, const mp_obj_t *a) { + int v[5]; + picogame_canvas_rect(cv_args(a, n, v), v[0], v[1], v[2], v[3], v[4]); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canvas_rect_obj, 6, 6, canvas_rect); + +//| +//| def line(self, x0: int, y0: int, x1: int, y1: int, color: int) -> None: +//| """Draw a one-pixel line between two points.""" +//| ... +//| +static mp_obj_t canvas_line(size_t n, const mp_obj_t *a) { + int v[5]; + picogame_canvas_line(cv_args(a, n, v), v[0], v[1], v[2], v[3], v[4]); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canvas_line_obj, 6, 6, canvas_line); + +//| +//| def fill_circle(self, cx: int, cy: int, r: int, color: int) -> None: +//| """Fill a circle of radius ``r`` centered on ``(cx, cy)``.""" +//| ... +//| +static mp_obj_t canvas_fill_circle(size_t n, const mp_obj_t *a) { + int v[4]; + picogame_canvas_fill_circle(cv_args(a, n, v), v[0], v[1], v[2], v[3]); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canvas_fill_circle_obj, 5, 5, canvas_fill_circle); + +//| +//| def circle(self, cx: int, cy: int, r: int, color: int) -> None: +//| """Draw a one-pixel circle outline.""" +//| ... +//| +static mp_obj_t canvas_circle(size_t n, const mp_obj_t *a) { + int v[4]; + picogame_canvas_circle(cv_args(a, n, v), v[0], v[1], v[2], v[3]); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canvas_circle_obj, 5, 5, canvas_circle); + +//| +//| def ring(self, cx: int, cy: int, r: int, thickness: int, color: int) -> None: +//| """Draw a circle outline ``thickness`` pixels wide, grown inwards from radius ``r``.""" +//| ... +//| +static mp_obj_t canvas_ring(size_t n, const mp_obj_t *a) { + int v[5]; + picogame_canvas_ring(cv_args(a, n, v), v[0], v[1], v[2], v[3], v[4]); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canvas_ring_obj, 6, 6, canvas_ring); + +//| +//| def triangle( +//| self, x0: int, y0: int, x1: int, y1: int, x2: int, y2: int, color: int +//| ) -> None: +//| """Draw a one-pixel triangle outline through the three points.""" +//| ... +//| +static mp_obj_t canvas_triangle(size_t n, const mp_obj_t *a) { + int v[7]; + picogame_canvas_triangle(cv_args(a, n, v), v[0], v[1], v[2], v[3], v[4], v[5], v[6]); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canvas_triangle_obj, 8, 8, canvas_triangle); + +//| +//| def fill_triangle( +//| self, x0: int, y0: int, x1: int, y1: int, x2: int, y2: int, color: int +//| ) -> None: +//| """Fill a triangle. See :py:meth:`fill_triangles` to submit a whole batch +//| in one call.""" +//| ... +//| +static mp_obj_t canvas_fill_triangle(size_t n, const mp_obj_t *a) { + int v[7]; + picogame_canvas_fill_triangle(cv_args(a, n, v), v[0], v[1], v[2], v[3], v[4], v[5], v[6]); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canvas_fill_triangle_obj, 8, 8, canvas_fill_triangle); + +//| +//| def ellipse(self, cx: int, cy: int, rx: int, ry: int, color: int) -> None: +//| """Draw a one-pixel ellipse outline with radii ``rx``/``ry``.""" +//| ... +//| +static mp_obj_t canvas_ellipse(size_t n, const mp_obj_t *a) { + int v[5]; + picogame_canvas_ellipse(cv_args(a, n, v), v[0], v[1], v[2], v[3], v[4]); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canvas_ellipse_obj, 6, 6, canvas_ellipse); + +//| +//| def fill_ellipse(self, cx: int, cy: int, rx: int, ry: int, color: int) -> None: +//| """Fill an ellipse with radii ``rx``/``ry``.""" +//| ... +//| +static mp_obj_t canvas_fill_ellipse(size_t n, const mp_obj_t *a) { + int v[5]; + picogame_canvas_fill_ellipse(cv_args(a, n, v), v[0], v[1], v[2], v[3], v[4]); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canvas_fill_ellipse_obj, 6, 6, canvas_fill_ellipse); + +//| +//| def fill_round_rect(self, x: int, y: int, w: int, h: int, r: int, color: int) -> None: +//| """Fill a rectangle with corners rounded to radius ``r``.""" +//| ... +//| +static mp_obj_t canvas_fill_round_rect(size_t n, const mp_obj_t *a) { + int v[6]; + picogame_canvas_fill_round_rect(cv_args(a, n, v), v[0], v[1], v[2], v[3], v[4], v[5]); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canvas_fill_round_rect_obj, 7, 7, canvas_fill_round_rect); + +//| +//| def frame3d(self, x: int, y: int, w: int, h: int, light: int, dark: int) -> None: +//| """Draw a one-pixel bevelled frame: top and left edges in ``light``, bottom and +//| right in ``dark``, giving a raised look (swap the two for a sunken one).""" +//| ... +//| +static mp_obj_t canvas_frame3d(size_t n, const mp_obj_t *a) { + int v[6]; + picogame_canvas_frame3d(cv_args(a, n, v), v[0], v[1], v[2], v[3], v[4], v[5]); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canvas_frame3d_obj, 7, 7, canvas_frame3d); + +//| +//| def text( +//| self, x: int, y: int, s: str, fg: int, font: fontio.BuiltinFont, bg: int | None = None +//| ) -> None: +//| """Draw ``s`` into the surface, rasterizing each glyph from ``font`` as it +//| is drawn; no memory is retained between calls. Only ASCII characters are +//| supported. If ``bg`` is given the glyph background is filled with it, +//| otherwise it is transparent. Inside a `StripDraw` callback the view is a +//| Canvas, so ``view.text(...)`` draws text directly into the frame.""" +//| ... +//| +static mp_obj_t canvas_text(size_t n, const mp_obj_t *a) { + const char *s = mp_obj_str_get_str(a[3]); + mp_int_t fg = mp_obj_get_int(a[4]); + const void *font = MP_OBJ_TO_PTR(mp_arg_validate_type(a[5], &fontio_builtinfont_type, MP_QSTR_font)); + bool has_bg = (n >= 7) && (a[6] != mp_const_none); + uint16_t bg = has_bg ? (uint16_t)mp_obj_get_int(a[6]) : 0; + picogame_canvas_text(cv_self(a[0]), mp_obj_get_int(a[1]), mp_obj_get_int(a[2]), + s, (uint16_t)fg, bg, has_bg, font); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canvas_text_obj, 6, 7, canvas_text); + +//| def move(self, x: int, y: int) -> None: +//| """Move the surface's top-left corner to (x, y) and mark both the old and new area dirty.""" +//| ... +//| +static mp_obj_t canvas_move(mp_obj_t self_in, mp_obj_t x_in, mp_obj_t y_in) { + picogame_canvas_obj_t *self = cv_self(self_in); + int nx = mp_obj_get_int(x_in), ny = mp_obj_get_int(y_in); + if (nx == self->x && ny == self->y) { + return mp_const_none; // unchanged -> avoid an avoidable repaint + } + // dirty old + new extents so the move repaints both + picogame_canvas_dirty_union(self, self->x, self->y, self->x + self->w, self->y + self->h); + self->x = nx; + self->y = ny; + picogame_canvas_dirty_union(self, self->x, self->y, self->x + self->w, self->y + self->h); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_3(canvas_move_obj, canvas_move); + +//| +//| x: int +//| """Horizontal position of the canvas top-left corner. (read-only) +//| +//| Set with :py:meth:`move`.""" +//| y: int +//| """Vertical position of the canvas top-left corner. (read-only) +//| +//| Set with :py:meth:`move`.""" +//| width: int +//| """Surface width in pixels. (read-only)""" +//| height: int +//| """Surface height in pixels. (read-only)""" +//| +//| +static mp_obj_t canvas_get_x(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(cv_self(self_in)->x); +} +static MP_DEFINE_CONST_FUN_OBJ_1(canvas_get_x_obj, canvas_get_x); +MP_PROPERTY_GETTER(canvas_x_obj, (mp_obj_t)&canvas_get_x_obj); + +static mp_obj_t canvas_get_y(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(cv_self(self_in)->y); +} +static MP_DEFINE_CONST_FUN_OBJ_1(canvas_get_y_obj, canvas_get_y); +MP_PROPERTY_GETTER(canvas_y_obj, (mp_obj_t)&canvas_get_y_obj); + +static mp_obj_t canvas_get_width(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(cv_self(self_in)->w); +} +static MP_DEFINE_CONST_FUN_OBJ_1(canvas_get_width_obj, canvas_get_width); +MP_PROPERTY_GETTER(canvas_width_obj, (mp_obj_t)&canvas_get_width_obj); + +static mp_obj_t canvas_get_height(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(cv_self(self_in)->h); +} +static MP_DEFINE_CONST_FUN_OBJ_1(canvas_get_height_obj, canvas_get_height); +MP_PROPERTY_GETTER(canvas_height_obj, (mp_obj_t)&canvas_get_height_obj); + +static const mp_rom_map_elem_t picogame_canvas_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&canvas_clear_obj) }, + { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&canvas_x_obj) }, + { MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&canvas_y_obj) }, + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&canvas_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&canvas_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&canvas_pixel_obj) }, + { MP_ROM_QSTR(MP_QSTR_fill_rect), MP_ROM_PTR(&canvas_fill_rect_obj) }, + { MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(&canvas_blit_obj) }, + { MP_ROM_QSTR(MP_QSTR_mode7), MP_ROM_PTR(&canvas_mode7_obj) }, + { MP_ROM_QSTR(MP_QSTR_fill_triangles), MP_ROM_PTR(&canvas_fill_triangles_obj) }, + { MP_ROM_QSTR(MP_QSTR_vspans), MP_ROM_PTR(&canvas_vspans_obj) }, + { MP_ROM_QSTR(MP_QSTR_road), MP_ROM_PTR(&canvas_road_obj) }, + { MP_ROM_QSTR(MP_QSTR_rect), MP_ROM_PTR(&canvas_rect_obj) }, + { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&canvas_line_obj) }, + { MP_ROM_QSTR(MP_QSTR_fill_circle), MP_ROM_PTR(&canvas_fill_circle_obj) }, + { MP_ROM_QSTR(MP_QSTR_circle), MP_ROM_PTR(&canvas_circle_obj) }, + { MP_ROM_QSTR(MP_QSTR_ring), MP_ROM_PTR(&canvas_ring_obj) }, + { MP_ROM_QSTR(MP_QSTR_triangle), MP_ROM_PTR(&canvas_triangle_obj) }, + { MP_ROM_QSTR(MP_QSTR_fill_triangle), MP_ROM_PTR(&canvas_fill_triangle_obj) }, + { MP_ROM_QSTR(MP_QSTR_ellipse), MP_ROM_PTR(&canvas_ellipse_obj) }, + { MP_ROM_QSTR(MP_QSTR_fill_ellipse), MP_ROM_PTR(&canvas_fill_ellipse_obj) }, + { MP_ROM_QSTR(MP_QSTR_fill_round_rect), MP_ROM_PTR(&canvas_fill_round_rect_obj) }, + { MP_ROM_QSTR(MP_QSTR_frame3d), MP_ROM_PTR(&canvas_frame3d_obj) }, + { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&canvas_text_obj) }, + { MP_ROM_QSTR(MP_QSTR_move), MP_ROM_PTR(&canvas_move_obj) }, +}; +static MP_DEFINE_CONST_DICT(picogame_canvas_locals_dict, picogame_canvas_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + picogame_canvas_type, + MP_QSTR_Canvas, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, picogame_canvas_make_new, + locals_dict, &picogame_canvas_locals_dict + ); diff --git a/shared-bindings/picogame/Canvas.h b/shared-bindings/picogame/Canvas.h new file mode 100644 index 00000000000..fb37aa9f898 --- /dev/null +++ b/shared-bindings/picogame/Canvas.h @@ -0,0 +1,11 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-module/picogame/Canvas.h" + +extern const mp_obj_type_t picogame_canvas_type; diff --git a/shared-bindings/picogame/Display.c b/shared-bindings/picogame/Display.c new file mode 100644 index 00000000000..deea95c70b2 --- /dev/null +++ b/shared-bindings/picogame/Display.c @@ -0,0 +1,127 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#include "py/runtime.h" // brings in the board config (CIRCUITPY_PICOGAME_FAST_DISPLAY) + +// The fast DMA Display backend is port-specific (needs a common-hal). Ports without it +// (e.g. ESP32) build picogame with this whole type compiled out and use Scene's portable +// bus.send renderer instead - so picogame stays buildable on any CircuitPython port. +#if CIRCUITPY_PICOGAME_FAST_DISPLAY +#include "shared-bindings/picogame/Display.h" +#include "shared-bindings/picogame/Sprite.h" +#include "shared-bindings/busdisplay/BusDisplay.h" +#include "shared-module/picogame/__init__.h" +#include "common-hal/picogame/Display.h" + +//| class Display: +//| """An accelerated display backend that wraps an existing +//| :py:class:`~busdisplay.BusDisplay` and sends pixels with asynchronous, +//| double-buffered DMA. It reuses the wrapped display's bus, window commands +//| and dimensions. +//| +//| Constructing it raises :py:class:`NotImplementedError` when +//| :py:data:`FAST_DISPLAY_SUPPORTED` is `False`.""" +//| +//| def __init__(self, display: busdisplay.BusDisplay, *, rgb444: bool = False) -> None: +//| """:param ~busdisplay.BusDisplay display: the display to wrap +//| :param bool rgb444: drive the panel in 12-bit RGB444 instead of 16-bit +//| RGB565, reducing bus traffic at the cost of color depth (4,096 colors +//| instead of 65,536). The panel controller must support 12-bit color; +//| ST7789 and ST7735 do, ILI9341 does not. See :py:data:`RGB444_SUPPORTED`.""" +//| ... +//| +static mp_obj_t picogame_display_make_new(const mp_obj_type_t *type, size_t n_args, + size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_display, ARG_rgb444 }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_display, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_rgb444, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t native = mp_obj_cast_to_native_base(args[ARG_display].u_obj, &busdisplay_busdisplay_type); + if (!mp_obj_is_type(native, &busdisplay_busdisplay_type)) { + mp_arg_validate_type(native, &busdisplay_busdisplay_type, MP_QSTR_display); + } + picogame_display_obj_t *self = mp_obj_malloc(picogame_display_obj_t, type); + common_hal_picogame_display_construct(self, MP_OBJ_TO_PTR(native), args[ARG_rgb444].u_bool); + return MP_OBJ_FROM_PTR(self); +} + +//| def render( +//| self, +//| sprites: List[Sprite], +//| buffer_a: WriteableBuffer, +//| buffer_b: WriteableBuffer, +//| x0: int, +//| y0: int, +//| x1: int, +//| y1: int, +//| *, +//| background: int = 0, +//| ) -> None: +//| """Render ``sprites`` into the screen region from ``(x0, y0)`` up to, but +//| not including, ``(x1, y1)``, and send it with asynchronous DMA. ``buffer_a`` and ``buffer_b`` are two +//| equal strip buffers of at least ``(x1 - x0) * 2`` bytes each, used for +//| double buffering. +//| +//| This method accepts sprites only. For mixed layer kinds use a `Scene` or +//| the module-level :py:func:`render`.""" +//| ... +//| +//| +static mp_obj_t picogame_display_render(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sprites, ARG_buffer_a, ARG_buffer_b, ARG_x0, ARG_y0, ARG_x1, ARG_y1, ARG_background }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sprites, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_buffer_a, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_buffer_b, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_x0, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_y0, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_background, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + }; + picogame_display_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + size_t n = 0; + mp_obj_t *items; + mp_obj_get_array(args[ARG_sprites].u_obj, &n, &items); + for (size_t i = 0; i < n; i++) { + mp_arg_validate_type(items[i], &picogame_sprite_type, MP_QSTR_sprite); + } + + mp_buffer_info_t ba, bb; + mp_get_buffer_raise(args[ARG_buffer_a].u_obj, &ba, MP_BUFFER_WRITE); + mp_get_buffer_raise(args[ARG_buffer_b].u_obj, &bb, MP_BUFFER_WRITE); + size_t buf_pixels = (ba.len < bb.len ? ba.len : bb.len) / 2; + + // kinds == NULL: every item is a sprite (the layered path handles this). + common_hal_picogame_display_render(self, items, NULL, n, + (uint16_t *)ba.buf, (uint16_t *)bb.buf, buf_pixels, + args[ARG_x0].u_int, args[ARG_y0].u_int, args[ARG_x1].u_int, args[ARG_y1].u_int, + args[ARG_background].u_int, 0, 0); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_KW(picogame_display_render_obj, 8, picogame_display_render); + +static const mp_rom_map_elem_t picogame_display_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_render), MP_ROM_PTR(&picogame_display_render_obj) }, +}; +static MP_DEFINE_CONST_DICT(picogame_display_locals_dict, picogame_display_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + picogame_display_type, + MP_QSTR_Display, + MP_TYPE_FLAG_NONE, + make_new, picogame_display_make_new, + locals_dict, &picogame_display_locals_dict + ); + +#endif // CIRCUITPY_PICOGAME_FAST_DISPLAY diff --git a/shared-bindings/picogame/Display.h b/shared-bindings/picogame/Display.h new file mode 100644 index 00000000000..6034124d88d --- /dev/null +++ b/shared-bindings/picogame/Display.h @@ -0,0 +1,13 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#pragma once + +#if CIRCUITPY_PICOGAME_FAST_DISPLAY +#include "common-hal/picogame/Display.h" +#endif + +extern const mp_obj_type_t picogame_display_type; diff --git a/shared-bindings/picogame/Framebuffer.c b/shared-bindings/picogame/Framebuffer.c new file mode 100644 index 00000000000..05b1612f190 --- /dev/null +++ b/shared-bindings/picogame/Framebuffer.c @@ -0,0 +1,170 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT +// +// picogame.Framebuffer: a RAM render target used in place of a BusDisplay. + +#include "py/runtime.h" +#include "shared-module/picogame/pg_compat.h" +#include "shared-bindings/busdisplay/BusDisplay.h" +#include "shared-bindings/picogame/__init__.h" +#include "shared-bindings/picogame/Bitmap.h" +#include "shared-bindings/picogame/Sprite.h" +#if CIRCUITPY_PICOGAME_FAST_DISPLAY +#include "shared-bindings/picogame/Display.h" // fast DMA backend; absent on portable ports +#include "common-hal/picogame/Display.h" // its struct (pg_get_display unwraps the wrapper) +#endif +#include "shared-bindings/picogame/Scene.h" +#include "shared-bindings/picogame/Tilemap.h" +#include "shared-bindings/picogame/Particles.h" +#include "shared-bindings/picogame/Canvas.h" +#include "shared-bindings/picogame/Framebuffer.h" +#include "shared-module/picogame/__init__.h" +#include "shared-module/picogame/Bitmap.h" +#include "shared-module/picogame/Sprite.h" + +#if CIRCUITPY_PICOGAME_FRAMEBUFFER +// --------------------------------------------------------------------------- +// Framebuffer (a RAM render target used in place of a BusDisplay; scanout-buffer +// platforms - WASM playground, desktop sim, FruitJam DVI/HSTX) +// --------------------------------------------------------------------------- +//| class Framebuffer: +//| """A RAM framebuffer render target that a `Scene` or :py:func:`render` can +//| draw into instead of a :py:class:`~busdisplay.BusDisplay`, for example the +//| scanout buffer of a :py:class:`picodvi.Framebuffer`. +//| +//| Constructing it raises :py:class:`NotImplementedError` when +//| :py:data:`FRAMEBUFFER_SUPPORTED` is `False`.""" +//| +//| def __init__( +//| self, +//| buffer: WriteableBuffer, +//| width: int, +//| height: int, +//| *, +//| native_rgb565: bool = False, +//| rgb332: bool = False, +//| ) -> None: +//| """:param ~circuitpython_typing.WriteableBuffer buffer: caller-owned target +//| buffer of at least ``width * height * 2`` bytes, or ``width * height`` +//| bytes with ``rgb332=True`` +//| :param int width: target width in pixels +//| :param int height: target height in pixels +//| :param bool native_rgb565: fill the buffer with native-endian RGB565, the +//| format 16-bit scanout targets expect. By default the buffer holds +//| transfer-order RGB565. +//| :param bool rgb332: fill the buffer with 8-bit RGB332, the format of 8-bit +//| scanout targets +//| +//| ``native_rgb565`` and ``rgb332`` cannot both be true. Bitmaps, palettes and +//| :py:func:`rgb565` values stay transfer-order RGB565 regardless of the +//| output format.""" +//| ... +//| +//| width: int +//| """Target width in pixels. (read-only)""" +//| height: int +//| """Target height in pixels. (read-only)""" +//| +//| +// Static SRAM compose strip (see the scratch comment in make_new). 640*16*2 = 20 KB .bss, +// only on CIRCUITPY_PICOGAME_FRAMEBUFFER builds (fb boards have the SRAM to spare). +#define PICOGAME_FB_SCRATCH_MAX_W 640 +static uint16_t picogame_fb_scratch_sram[PICOGAME_FB_SCRATCH_MAX_W * PICOGAME_FB_SCRATCH_H]; + +static mp_obj_t picogame_framebuffer_make_new(const mp_obj_type_t *type, size_t n_args, + size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_buffer, ARG_width, ARG_height, ARG_native_rgb565, ARG_rgb332 }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_width, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_height, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_native_rgb565, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_rgb332, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + mp_int_t width = mp_arg_validate_int_range(args[ARG_width].u_int, 1, 4096, MP_QSTR_width); + mp_int_t height = mp_arg_validate_int_range(args[ARG_height].u_int, 1, 4096, MP_QSTR_height); + if (args[ARG_native_rgb565].u_bool && args[ARG_rgb332].u_bool) { + mp_arg_error_invalid(MP_QSTR_format); // native_rgb565 and rgb332 are exclusive + } + bool rgb332 = args[ARG_rgb332].u_bool; + + mp_buffer_info_t bi; + mp_get_buffer_raise(args[ARG_buffer].u_obj, &bi, MP_BUFFER_WRITE); + uint64_t need = (uint64_t)width * (uint64_t)height * (rgb332 ? 1u : 2u); + if ((uint64_t)bi.len < need) { + mp_raise_ValueError(MP_ERROR_TEXT("buffer too small")); + } + + picogame_framebuffer_obj_t *self = mp_obj_malloc(picogame_framebuffer_obj_t, type); + self->buffer = args[ARG_buffer].u_obj; + self->fb = (uint16_t *)bi.buf; + self->width = width; + self->height = height; + self->fmt = rgb332 ? PICOGAME_FB_RGB332 + : (args[ARG_native_rgb565].u_bool ? PICOGAME_FB_NATIVE565 : PICOGAME_FB_WIRE565); + // A LIVE scanout buffer (picodvi/HDMI) is read continuously, so picogame_render_framebuffer + // composes each band into this PRIVATE strip and only memcpys the FINISHED band into the fb. + // That serves BOTH targets: (a) native -> also byte-swap the strip so the fb never holds wire + // (no pink); (b) wire -> no swap, but the off-screen compose still stops the beam from sampling + // a half-composited region (background filled, sprite not yet drawn) = no sprite/HUD flicker. + // Always allocated for the FB target; the WASM/sim path (read out after present, not live) just + // pays a small strip + one memcpy. See PICOGAME_FB_SCRATCH_H. + // + // The scratch must be FAST memory: on a PSRAM-heap board (Fruit Jam) a heap bytearray + // lands in external PSRAM and every compose write pays QSPI latency (measured 8.7 vs + // 64+ MB/s SRAM; a full-res StripDraw frame ballooned refresh to ~30-38 ms). One static + // SRAM strip serves every Framebuffer (compose is synchronous) up to 640 px wide; wider + // targets fall back to the heap. + self->scratch_buf = mp_const_none; + self->scratch = NULL; + self->scratch_rows = 0; + { + int rows = PICOGAME_FB_SCRATCH_H; + if (rows > height) { + rows = height; + } + if (width <= PICOGAME_FB_SCRATCH_MAX_W) { + self->scratch = picogame_fb_scratch_sram; + } else { + mp_obj_t sb = mp_obj_new_bytearray_of_zeros((size_t)width * (size_t)rows * 2u); + mp_buffer_info_t sbi; + mp_get_buffer_raise(sb, &sbi, MP_BUFFER_WRITE); + self->scratch_buf = sb; + self->scratch = (uint16_t *)sbi.buf; + } + self->scratch_rows = rows; + } + return MP_OBJ_FROM_PTR(self); +} + +static mp_obj_t picogame_framebuffer_get_width(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(((picogame_framebuffer_obj_t *)MP_OBJ_TO_PTR(self_in))->width); +} +static MP_DEFINE_CONST_FUN_OBJ_1(picogame_framebuffer_get_width_obj, picogame_framebuffer_get_width); +MP_PROPERTY_GETTER(picogame_framebuffer_width_obj, (mp_obj_t)&picogame_framebuffer_get_width_obj); + +static mp_obj_t picogame_framebuffer_get_height(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(((picogame_framebuffer_obj_t *)MP_OBJ_TO_PTR(self_in))->height); +} +static MP_DEFINE_CONST_FUN_OBJ_1(picogame_framebuffer_get_height_obj, picogame_framebuffer_get_height); +MP_PROPERTY_GETTER(picogame_framebuffer_height_obj, (mp_obj_t)&picogame_framebuffer_get_height_obj); + +static const mp_rom_map_elem_t picogame_framebuffer_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&picogame_framebuffer_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&picogame_framebuffer_height_obj) }, +}; +static MP_DEFINE_CONST_DICT(picogame_framebuffer_locals_dict, picogame_framebuffer_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + picogame_framebuffer_type, + MP_QSTR_Framebuffer, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, picogame_framebuffer_make_new, + locals_dict, &picogame_framebuffer_locals_dict + ); +#endif // CIRCUITPY_PICOGAME_FRAMEBUFFER diff --git a/shared-bindings/picogame/Framebuffer.h b/shared-bindings/picogame/Framebuffer.h new file mode 100644 index 00000000000..99112b8fa03 --- /dev/null +++ b/shared-bindings/picogame/Framebuffer.h @@ -0,0 +1,11 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-module/picogame/__init__.h" // picogame_framebuffer_obj_t + +extern const mp_obj_type_t picogame_framebuffer_type; diff --git a/shared-bindings/picogame/Particles.c b/shared-bindings/picogame/Particles.c new file mode 100644 index 00000000000..bc7f286b46a --- /dev/null +++ b/shared-bindings/picogame/Particles.c @@ -0,0 +1,137 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#include "py/runtime.h" +#include "shared-module/picogame/pg_compat.h" +#include "shared-bindings/picogame/Particles.h" +#include "shared-module/picogame/Particles.h" + +static void reset_dirty(picogame_particles_obj_t *self) { + // INT32 sentinels (not int16): bbox fields are int32 + positions 24.8, so a big-world emitter past + // +-32767 px must still accumulate. (Matches picogame_dirty_reset; update()/clear() use it too.) + self->cx1 = self->px1 = 0x7fffffff; + self->cy1 = self->py1 = 0x7fffffff; + self->cx2 = self->px2 = -0x7fffffff - 1; + self->cy2 = self->py2 = -0x7fffffff - 1; +} + +//| class Particles: +//| """A pooled particle layer (small moving dots), drawn as one Scene layer. +//| Add it to a Scene, ``emit()`` bursts, and call ``tick()`` each frame.""" +//| +//| def __init__( +//| self, capacity: int, *, size: int = 1, gravity: float = 0.0, fade: bool = False +//| ) -> None: +//| """``capacity`` is how many particles may be alive at once; the pool is +//| allocated once here and never grows. If :py:meth:`emit` would exceed it, +//| the excess particles are dropped. +//| +//| ``size`` is the square side of one particle in pixels. ``gravity`` is added to +//| each particle's vertical speed every :py:meth:`tick`. ``fade=True`` dims +//| particles towards the end of their life instead of letting them vanish at full +//| brightness.""" +//| ... +//| +static mp_obj_t picogame_particles_make_new(const mp_obj_type_t *type, size_t n_args, + size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_capacity, ARG_size, ARG_gravity, ARG_fade }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_capacity, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_size, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, + { MP_QSTR_gravity, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_fade, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_int_t cap = mp_arg_validate_int_range(args[ARG_capacity].u_int, 1, 4096, MP_QSTR_capacity); + mp_int_t size = mp_arg_validate_int_range(args[ARG_size].u_int, 1, 8, MP_QSTR_size); + mp_float_t gravity = (args[ARG_gravity].u_obj == mp_const_none) + ? 0.0f : mp_obj_get_float(args[ARG_gravity].u_obj); + if (gravity > 127.99f) { // clamp to the int16 8.8 range: gravity*256 must fit + gravity = 127.99f; // [-32768, 32767]; |g|>=128 would flip the sign + } else if (gravity < -128.0f) { + gravity = -128.0f; + } + + picogame_particles_obj_t *self = mp_obj_malloc(picogame_particles_obj_t, type); + self->cap = cap; + self->count = 0; + self->size = size; + self->gravity = (int16_t)(gravity * 256); + self->fade = args[ARG_fade].u_bool; + // Pure numeric arrays, no Python pointers -> exempt from the conservative GC scan + // (shorter gc.collect() pauses; fixed at construction, never m_renew'd). + self->px = m_malloc_without_collect(cap * sizeof(int32_t)); + self->py = m_malloc_without_collect(cap * sizeof(int32_t)); + self->vx = m_malloc_without_collect(cap * sizeof(int16_t)); + self->vy = m_malloc_without_collect(cap * sizeof(int16_t)); + self->life = m_malloc_without_collect(cap * sizeof(uint16_t)); + self->life0 = m_malloc_without_collect(cap * sizeof(uint16_t)); + self->color = m_malloc_without_collect(cap * sizeof(uint16_t)); + reset_dirty(self); + return MP_OBJ_FROM_PTR(self); +} + +//| +//| def emit( +//| self, x: int, y: int, count: int, speed: int = 1, life: int = 30, color: int = 0xFFFF +//| ) -> None: +//| """Spawn ``count`` particles at ``(x, y)``, living ``life`` ticks, in +//| ``color`` (default white). Each particle's horizontal and vertical velocity is chosen +//| independently from ``-speed`` through ``speed`` pixels per tick.""" +//| ... +//| +static mp_obj_t picogame_particles_emit_fun(size_t n_args, const mp_obj_t *args) { + picogame_particles_obj_t *self = MP_OBJ_TO_PTR(args[0]); + int x = mp_obj_get_int(args[1]); + int y = mp_obj_get_int(args[2]); + int count = mp_arg_validate_int_min(mp_obj_get_int(args[3]), 0, MP_QSTR_count); + // speed*256 must fit the int16_t velocity (8.8) -> cap 127; life is stored as uint16_t. + int speed = (n_args > 4) ? mp_arg_validate_int_range(mp_obj_get_int(args[4]), 0, 127, MP_QSTR_speed) : 1; + int life = (n_args > 5) ? mp_arg_validate_int_range(mp_obj_get_int(args[5]), 1, 65535, MP_QSTR_life) : 30; + uint16_t color = (n_args > 6) + ? mp_arg_validate_int_range(mp_obj_get_int(args[6]), 0, 0xFFFF, MP_QSTR_color) : 0xFFFF; + picogame_particles_emit(self, x, y, count, speed, life, color); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picogame_particles_emit_obj, 4, 7, picogame_particles_emit_fun); + +//| def tick(self) -> None: +//| """Advance all particles one step (movement, gravity, aging).""" +//| ... +//| +static mp_obj_t picogame_particles_tick(mp_obj_t self_in) { + picogame_particles_update(MP_OBJ_TO_PTR(self_in)); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(picogame_particles_tick_obj, picogame_particles_tick); + +//| def clear(self) -> None: +//| """Remove all particles.""" +//| ... +//| +//| +static mp_obj_t picogame_particles_clear_method(mp_obj_t self_in) { + picogame_particles_clear(MP_OBJ_TO_PTR(self_in)); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(picogame_particles_clear_obj, picogame_particles_clear_method); + +static const mp_rom_map_elem_t picogame_particles_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_emit), MP_ROM_PTR(&picogame_particles_emit_obj) }, + { MP_ROM_QSTR(MP_QSTR_tick), MP_ROM_PTR(&picogame_particles_tick_obj) }, + { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&picogame_particles_clear_obj) }, +}; +static MP_DEFINE_CONST_DICT(picogame_particles_locals_dict, picogame_particles_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + picogame_particles_type, + MP_QSTR_Particles, + MP_TYPE_FLAG_NONE, + make_new, picogame_particles_make_new, + locals_dict, &picogame_particles_locals_dict + ); diff --git a/shared-bindings/picogame/Particles.h b/shared-bindings/picogame/Particles.h new file mode 100644 index 00000000000..8f8b37ab819 --- /dev/null +++ b/shared-bindings/picogame/Particles.h @@ -0,0 +1,11 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-module/picogame/Particles.h" + +extern const mp_obj_type_t picogame_particles_type; diff --git a/shared-bindings/picogame/Scene.c b/shared-bindings/picogame/Scene.c new file mode 100644 index 00000000000..02bfffe843e --- /dev/null +++ b/shared-bindings/picogame/Scene.c @@ -0,0 +1,567 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#include "py/runtime.h" +#include +#include "shared-module/picogame/pg_compat.h" +#include "py/objtuple.h" +#include "py/objlist.h" +#include "shared-bindings/picogame/Scene.h" +#include "shared-bindings/picogame/__init__.h" +#include "shared-bindings/picogame/Sprite.h" +#include "shared-bindings/picogame/Tilemap.h" +#include "shared-bindings/picogame/Particles.h" +#include "shared-bindings/picogame/Canvas.h" +#include "shared-bindings/picogame/Framebuffer.h" +#include "shared-bindings/busdisplay/BusDisplay.h" +#if CIRCUITPY_PICOGAME_FAST_DISPLAY +#include "shared-bindings/picogame/Display.h" +#endif +#include "shared-module/picogame/Scene.h" +#include "shared-module/picogame/__init__.h" +#include "shared-module/picogame/Tilemap.h" +#include "shared-module/picogame/Particles.h" +#include "shared-module/picogame/Canvas.h" +#if CIRCUITPY_PICOGAME_FAST_DISPLAY +#include "common-hal/picogame/Display.h" +#endif + +#define SCENE_INIT_CAP 8 +#define PICOGAME_MAX_DIRTY_RECTS 6 // separate regions repainted per refresh + +// Classify the `display` arg into a render backend and return the object Scene should +// store: a fast picogame.Display (DMA), a picogame.Framebuffer (RAM scanout buffer, when +// built in), or a plain busdisplay (portable bus.send). Sets *fast / *fb_target +// accordingly; for a busdisplay it accepts a SUBCLASS (e.g. adafruit_st7789.ST7789) by +// casting to its native base and returns that. Raises TypeError otherwise. This is where +// the flag-gated type checks live, so the constructor body stays clean. +static mp_obj_t scene_resolve_target(mp_obj_t disp, bool *fast, bool *fb_target) { + *fast = false; + *fb_target = false; + #if CIRCUITPY_PICOGAME_FAST_DISPLAY + { + // Accept subclasses too (same as the busdisplay path below): resolve to the + // native base object, which the C renderer can safely cast. + mp_obj_t ndisp = mp_obj_cast_to_native_base(disp, &picogame_display_type); + if (ndisp != MP_OBJ_NULL && mp_obj_is_type(ndisp, &picogame_display_type)) { + *fast = true; + return ndisp; + } + } + #endif + #if CIRCUITPY_PICOGAME_FRAMEBUFFER + { + mp_obj_t nfb = mp_obj_cast_to_native_base(disp, &picogame_framebuffer_type); + if (nfb != MP_OBJ_NULL && mp_obj_is_type(nfb, &picogame_framebuffer_type)) { + *fb_target = true; // refresh() composites dirty rects straight into its RAM buffer + return nfb; + } + } + #endif + // Plain busdisplay: accept a subclass by casting to its native base; the portable + // renderer treats self->display as a busdisplay_busdisplay_obj_t directly. + mp_obj_t native = mp_obj_cast_to_native_base(disp, &busdisplay_busdisplay_type); + if (!mp_obj_is_type(native, &busdisplay_busdisplay_type)) { + mp_arg_validate_type(native, &busdisplay_busdisplay_type, MP_QSTR_display); + } + return native; +} + +//| class Scene: +//| """A retained-mode scene with dirty-rectangle rendering for a `Display`, +//| :py:class:`~busdisplay.BusDisplay` or `Framebuffer` target. Add layers once +//| (insertion order is bottom to top), mutate them each frame, then call +//| :py:meth:`refresh`; only the regions reported changed are repainted.""" +//| +//| def __init__( +//| self, +//| display: Union[Display, busdisplay.BusDisplay, Framebuffer], +//| buffer_a: Optional[WriteableBuffer] = None, +//| buffer_b: Optional[WriteableBuffer] = None, +//| *, +//| background: int = 0, +//| top: int = 0, +//| bottom: int = 0, +//| left: int = 0, +//| right: int = 0, +//| ) -> None: +//| """:param display: the render target +//| :param ~circuitpython_typing.WriteableBuffer buffer_a: a strip buffer, +//| typically ``display.width * STRIP_H * 2`` bytes. Its size sets the +//| strip height: each strip is ``size // (display.width * 2)`` rows. Two +//| buffers let the next strip be composited while the previous one is +//| being sent. On a `Framebuffer` target there are no strips and the +//| buffers are unused; both default to `None`. +//| :param ~circuitpython_typing.WriteableBuffer buffer_b: the second strip +//| buffer, sized like ``buffer_a`` +//| :param int background: color that exposed areas are cleared to +//| :param int top: rows at the top edge the scene never renders into +//| :param int bottom: rows at the bottom edge the scene never renders into +//| :param int left: columns at the left edge the scene never renders into +//| :param int right: columns at the right edge the scene never renders into +//| +//| The four border insets reserve screen edges for content the application +//| draws itself, for example with :py:func:`render`; the scene renders only +//| the inner rectangle and never repaints the border.""" +//| ... +//| +static mp_obj_t picogame_scene_make_new(const mp_obj_type_t *type, size_t n_args, + size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_display, ARG_buffer_a, ARG_buffer_b, ARG_background, + ARG_top, ARG_bottom, ARG_left, ARG_right }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_display, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_buffer_a, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_buffer_b, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_background, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_top, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, // reserved border insets: + { MP_QSTR_bottom, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, // the scene renders only the + { MP_QSTR_left, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, // inner play rect; the app + { MP_QSTR_right, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, // owns the border around it + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Resolve the render backend (fast Display / Framebuffer / busdisplay) - all the + // flag-gated type checks are concentrated in scene_resolve_target(). + bool fast, fb_target; + mp_obj_t disp = scene_resolve_target(args[ARG_display].u_obj, &fast, &fb_target); + + // The framebuffer path composites directly into the target, so it needs no strip + // buffers; the SPI/DMA paths do. Validate them only when they'll be used. (When + // CIRCUITPY_PICOGAME_FRAMEBUFFER is off, fb_target is always false -> always validated, + // as before.) + if (!fb_target) { + mp_buffer_info_t tmp; + mp_get_buffer_raise(args[ARG_buffer_a].u_obj, &tmp, MP_BUFFER_WRITE); + mp_get_buffer_raise(args[ARG_buffer_b].u_obj, &tmp, MP_BUFFER_WRITE); + } + + picogame_scene_obj_t *self = mp_obj_malloc(picogame_scene_obj_t, type); + self->display = disp; + self->fast = fast; + #if CIRCUITPY_PICOGAME_FRAMEBUFFER + self->fb_target = fb_target; + #endif + self->buf_a = args[ARG_buffer_a].u_obj; + self->buf_b = args[ARG_buffer_b].u_obj; + self->background = args[ARG_background].u_int; + self->count = 0; + self->cap = SCENE_INIT_CAP; + self->items = m_new(mp_obj_t, SCENE_INIT_CAP); + self->kinds = m_new(uint8_t, SCENE_INIT_CAP); + self->snap = m_new(picogame_snapshot_t, SCENE_INIT_CAP); + self->cleared = false; + self->ox = 0; + self->oy = 0; + self->top = args[ARG_top].u_int; + self->bottom = args[ARG_bottom].u_int; + self->left = args[ARG_left].u_int; + self->right = args[ARG_right].u_int; + mp_obj_t zeros[4] = { + MP_OBJ_NEW_SMALL_INT(0), MP_OBJ_NEW_SMALL_INT(0), + MP_OBJ_NEW_SMALL_INT(0), MP_OBJ_NEW_SMALL_INT(0), + }; + self->dirty_rect = mp_obj_new_list(4, zeros); // reused every refresh + return MP_OBJ_FROM_PTR(self); +} + +// On a full repaint, sync sprite snapshots to current and drain the layer +// dirties (tilemap/particles/canvas) so they don't re-report a stale region. +static void snapshot_sync(picogame_scene_obj_t *self) { + int a, b, c, d; + for (uint16_t i = 0; i < self->count; i++) { + uint8_t kind = self->kinds[i] & PICOGAME_KIND_MASK; + if (kind == PICOGAME_KIND_TILEMAP) { + picogame_tilemap_take_dirty(MP_OBJ_TO_PTR(self->items[i]), &a, &b, &c, &d); + } else if (kind == PICOGAME_KIND_PARTICLES) { + picogame_particles_take_dirty(MP_OBJ_TO_PTR(self->items[i]), &a, &b, &c, &d); + } else if (kind == PICOGAME_KIND_CANVAS) { + picogame_canvas_take_dirty(MP_OBJ_TO_PTR(self->items[i]), &a, &b, &c, &d); + } else if (kind == PICOGAME_KIND_STRIPDRAW) { + // Immediate-mode layer: no retained state to snapshot/drain. + } else if (kind == PICOGAME_KIND_TRIANGLES) { + // Screen-space batch: drain the count-set dirty so it doesn't re-report. + int e, f, g, hh; + picogame_dirty_take(&((picogame_triangles_obj_t *)MP_OBJ_TO_PTR(self->items[i]))->dx1, + &e, &f, &g, &hh); + } else { + picogame_sprite_obj_t *s = MP_OBJ_TO_PTR(self->items[i]); + picogame_bitmap_obj_t *bm = s->bitmap; + int ax1, ay1, ax2, ay2; + picogame_sprite_aabb(s, &ax1, &ay1, &ax2, &ay2); + self->snap[i].x = ax1; + self->snap[i].y = ay1; + self->snap[i].w = ax2 - ax1; + self->snap[i].h = ay2 - ay1; + self->snap[i].bitmap = (void *)bm; + self->snap[i].frame = s->frame; + self->snap[i].flags = s->flags; + self->snap[i].scale = s->scale; + self->snap[i].angle = s->angle; + self->snap[i].seq = s->seq; + self->snap[i].dither = s->dither; + self->snap[i].flash_color = s->flash_color; + } + } +} + +//| +//| def add( +//| self, item: Union[Sprite, Tilemap, Canvas, Particles, StripDraw, Triangles], *, fixed: bool = False +//| ) -> Union[Sprite, Tilemap, Canvas, Particles, StripDraw, Triangles]: +//| """Add a layer of any kind, drawn starting with the next refresh; insertion +//| order is bottom to top. Returns the added item. +//| +//| ``fixed=True`` pins the item to the screen so it ignores the view offset +//| set by :py:meth:`set_view`, for example for a HUD over a scrolling world. +//| `StripDraw` and `Triangles` layers always draw in screen coordinates and +//| are unaffected by both ``fixed`` and the view offset.""" +//| ... +//| +static void scene_add_one(picogame_scene_obj_t *self, mp_obj_t item_in, bool fixed) { + uint8_t kind; + kind = picogame_kind_of(item_in); + if (fixed) { + kind |= PICOGAME_KIND_FIXED; + } + if (self->count >= self->cap) { + if (self->cap >= 0x8000) { // next doubling overflows uint16_t -> m_renew(0) shrink + m_malloc_fail((size_t)self->cap * 2); // next doubling overflows the count type + } + uint16_t new_cap = self->cap * 2; + self->items = m_renew(mp_obj_t, self->items, self->cap, new_cap); + self->kinds = m_renew(uint8_t, self->kinds, self->cap, new_cap); + self->snap = m_renew(picogame_snapshot_t, self->snap, self->cap, new_cap); + self->cap = new_cap; + } + self->items[self->count] = item_in; + self->kinds[self->count] = kind; + // Snapshot starts all-zero ("invisible", nothing diffed against garbage - m_renew doesn't + // zero), so a newly added layer is detected as changed and drawn on the next refresh. + memset(&self->snap[self->count], 0, sizeof(picogame_snapshot_t)); + self->count++; + // Honour "drawn on the next refresh" for EVERY kind: the zeroed snapshot covers sprites, + // but a re-add()ed Canvas/Tilemap whose dirty flag was already consumed would otherwise + // stay invisible until some other change. add() is a cold path -> force a full repaint. + self->cleared = false; +} + +static mp_obj_t picogame_scene_add(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_item, ARG_fixed }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_item, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_fixed, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + scene_add_one(MP_OBJ_TO_PTR(pos_args[0]), args[ARG_item].u_obj, args[ARG_fixed].u_bool); + return args[ARG_item].u_obj; // constructive: return the added item for `x = scene.add(...)` +} +static MP_DEFINE_CONST_FUN_OBJ_KW(picogame_scene_add_obj, 2, picogame_scene_add); + +//| def add_all(self, items: Iterable[Union[Sprite, Tilemap, Canvas, Particles, StripDraw, Triangles]]) -> None: +//| """Add several layers at once, bottom to top in iteration order.""" +//| ... +//| +static mp_obj_t picogame_scene_add_all(mp_obj_t self_in, mp_obj_t iterable) { + picogame_scene_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t iter = mp_getiter(iterable, NULL); + mp_obj_t item; + while ((item = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { + scene_add_one(self, item, false); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(picogame_scene_add_all_obj, picogame_scene_add_all); + +//| def remove(self, item: Union[Sprite, Tilemap, Canvas, Particles, StripDraw, Triangles]) -> None: +//| """Remove a previously added item; the draw order of the rest is unchanged. +//| The next refresh repaints the scene, so the item leaves no ghost. The item +//| itself is untouched and may be added again later. Raises +//| :py:class:`ValueError` if the item is not in the scene.""" +//| ... +//| +static mp_obj_t picogame_scene_remove(mp_obj_t self_in, mp_obj_t item_in) { + picogame_scene_obj_t *self = MP_OBJ_TO_PTR(self_in); + for (uint16_t i = 0; i < self->count; i++) { + if (self->items[i] != item_in) { + continue; + } + self->count--; + for (uint16_t j = i; j < self->count; j++) { // keep draw order: shift the tail down + self->items[j] = self->items[j + 1]; + self->kinds[j] = self->kinds[j + 1]; + self->snap[j] = self->snap[j + 1]; + } + self->items[self->count] = mp_const_none; // release the GC reference + self->cleared = false; // full repaint next refresh: background covers where it was + return mp_const_none; + } + mp_arg_error_invalid(MP_QSTR_item); +} +static MP_DEFINE_CONST_FUN_OBJ_2(picogame_scene_remove_obj, picogame_scene_remove); + +//| def invalidate(self) -> None: +//| """Force the scene's whole render area to repaint on the next refresh.""" +//| ... +//| +static mp_obj_t picogame_scene_invalidate(mp_obj_t self_in) { + ((picogame_scene_obj_t *)MP_OBJ_TO_PTR(self_in))->cleared = false; + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(picogame_scene_invalidate_obj, picogame_scene_invalidate); + +//| def set_view(self, ox: int, oy: int) -> None: +//| """Set the screen position ``(ox, oy)`` of scene coordinate ``(0, 0)``: +//| a scene point ``(x, y)`` is drawn at ``(x + ox, y + oy)``. Use a constant +//| offset to center a small scene, or update it each frame to scroll, which +//| repaints the whole render area.""" +//| ... +//| +static mp_obj_t picogame_scene_set_view(mp_obj_t self_in, mp_obj_t ox_in, mp_obj_t oy_in) { + picogame_scene_obj_t *self = MP_OBJ_TO_PTR(self_in); + int ox = mp_obj_get_int(ox_in); + int oy = mp_obj_get_int(oy_in); + if (ox != self->ox || oy != self->oy) { + self->ox = ox; + self->oy = oy; + self->cleared = false; // the whole view shifted -> full repaint next refresh + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_3(picogame_scene_set_view_obj, picogame_scene_set_view); + +//| view: Tuple[int, int] +//| """The current view offset ``(ox, oy)`` as set by :py:meth:`set_view`. (read-only)""" +static mp_obj_t picogame_scene_get_view(mp_obj_t self_in) { + picogame_scene_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t t[2] = { MP_OBJ_NEW_SMALL_INT(self->ox), MP_OBJ_NEW_SMALL_INT(self->oy) }; + return mp_obj_new_tuple(2, t); +} +static MP_DEFINE_CONST_FUN_OBJ_1(picogame_scene_get_view_obj, picogame_scene_get_view); +MP_PROPERTY_GETTER(picogame_scene_view_obj, (mp_obj_t)&picogame_scene_get_view_obj); + +//| display: Union[Display, busdisplay.BusDisplay, Framebuffer] +//| """The render target this Scene was built with. (read-only)""" +//| +static mp_obj_t picogame_scene_get_display(mp_obj_t self_in) { + return ((picogame_scene_obj_t *)MP_OBJ_TO_PTR(self_in))->display; +} +static MP_DEFINE_CONST_FUN_OBJ_1(picogame_scene_get_display_obj, picogame_scene_get_display); +MP_PROPERTY_GETTER(picogame_scene_display_obj, (mp_obj_t)&picogame_scene_get_display_obj); + +// Compute the frame's dirty rectangles for a WxH target, clipped to the play rect +// [left, w-right) x [top, h-bottom). On the first refresh (or after invalidate) this is +// a single full-screen rect + a snapshot sync; afterwards it diffs against the previous +// frame. Returns the rect count written to `rects` (0 = nothing to repaint). +// Backend-agnostic, so the SPI/fast and framebuffer paths share the exact dirty logic. +static int scene_collect_dirty_rects(picogame_scene_obj_t *self, int w, int h, picogame_rect_t *rects) { + int nr; + if (self->cleared) { + nr = picogame_scene_compute_dirty_rects(self->items, self->kinds, self->snap, + self->count, w, h, self->ox, self->oy, rects, PICOGAME_MAX_DIRTY_RECTS); + if (nr == 0) { + return 0; + } + } else { + rects[0].x1 = 0; + rects[0].y1 = 0; + rects[0].x2 = w; + rects[0].y2 = h; + nr = 1; + snapshot_sync(self); + // NOTE: cleared flips to true only after the RENDER completes (see the callers) - + // the snapshots are already advanced here, so an exception mid-render would + // otherwise leave a partially painted frame that no later refresh repairs. + } + + // Clip every dirty rect to the play rect [left, w-right) x [top, h-bottom); the + // reserved border is the app's, so the scene never paints into it. Drop empty rects. + int pa_x1 = self->left; + int pa_x2 = w - self->right; + int pa_y1 = self->top; + int pa_y2 = h - self->bottom; + int kept = 0; + for (int i = 0; i < nr; i++) { + int rx1 = rects[i].x1 < pa_x1 ? pa_x1 : rects[i].x1; + int rx2 = rects[i].x2 > pa_x2 ? pa_x2 : rects[i].x2; + int ry1 = rects[i].y1 < pa_y1 ? pa_y1 : rects[i].y1; + int ry2 = rects[i].y2 > pa_y2 ? pa_y2 : rects[i].y2; + if (rx1 >= rx2 || ry1 >= ry2) { + continue; + } + rects[kept].x1 = rx1; + rects[kept].y1 = ry1; + rects[kept].x2 = rx2; + rects[kept].y2 = ry2; + kept++; + } + return kept; +} + +// Store the bounding union of `nr` rects into the reusable dirty_rect list (no per-frame +// tuple allocation) and return it. Shared by both refresh backends. +static mp_obj_t scene_store_dirty(picogame_scene_obj_t *self, const picogame_rect_t *rects, int nr, int w, int h) { + int ux1 = w, uy1 = h, ux2 = 0, uy2 = 0; + for (int i = 0; i < nr; i++) { + if (rects[i].x1 < ux1) { + ux1 = rects[i].x1; + } + if (rects[i].y1 < uy1) { + uy1 = rects[i].y1; + } + if (rects[i].x2 > ux2) { + ux2 = rects[i].x2; + } + if (rects[i].y2 > uy2) { + uy2 = rects[i].y2; + } + } + mp_obj_list_store(self->dirty_rect, MP_OBJ_NEW_SMALL_INT(0), MP_OBJ_NEW_SMALL_INT(ux1)); + mp_obj_list_store(self->dirty_rect, MP_OBJ_NEW_SMALL_INT(1), MP_OBJ_NEW_SMALL_INT(uy1)); + mp_obj_list_store(self->dirty_rect, MP_OBJ_NEW_SMALL_INT(2), MP_OBJ_NEW_SMALL_INT(ux2)); + mp_obj_list_store(self->dirty_rect, MP_OBJ_NEW_SMALL_INT(3), MP_OBJ_NEW_SMALL_INT(uy2)); + return self->dirty_rect; +} + +#if CIRCUITPY_PICOGAME_FRAMEBUFFER +// Retained-mode refresh against a RAM framebuffer target (scanout-buffer platforms: +// WASM playground, desktop sim, FruitJam DVI/HSTX). Same dirty-rect logic as the SPI +// path (via the shared helpers), but each rect is composited straight into the target +// with picogame_render_framebuffer - no strip buffers, no bus transaction. A latched +// StripDraw BaseException is re-raised (no bus to close). Self-contained here so +// picogame_scene_refresh() keeps its original SPI/fast shape. +static mp_obj_t scene_refresh_fb(picogame_scene_obj_t *self) { + picogame_framebuffer_obj_t *fbt = MP_OBJ_TO_PTR(self->display); + int w = fbt->width; + int h = fbt->height; + + // Emulated invert (pg.invert on a Framebuffer) just toggled -> recomposite the WHOLE frame so the + // negative flip covers the whole screen, not only this frame's dirty rects (like a panel INVON). + if (picogame_fb_take_invert_dirty()) { + self->cleared = false; + } + + picogame_rect_t rects[PICOGAME_MAX_DIRTY_RECTS]; + int nr = scene_collect_dirty_rects(self, w, h, rects); + if (nr == 0) { + return mp_const_none; + } + + // Snapshots are already advanced; stay in the needs-full-repaint state until the + // render completes, so a BaseException mid-render (Ctrl-C in a StripDraw) leaves a + // scene whose NEXT refresh repaints everything instead of keeping a torn frame. + self->cleared = false; + for (int i = 0; i < nr; i++) { + mp_obj_t exc = picogame_render_framebuffer(fbt->fb, fbt->width, fbt->height, fbt->fmt, + fbt->scratch, fbt->scratch_rows, + self->items, self->kinds, self->count, + rects[i].x1, rects[i].y1, rects[i].x2, rects[i].y2, + self->background, self->ox, self->oy); + if (exc != MP_OBJ_NULL) { + nlr_raise(MP_OBJ_TO_PTR(exc)); // cleared stays false -> full repaint next refresh + } + } + self->cleared = true; + return scene_store_dirty(self, rects, nr, w, h); +} +#endif // CIRCUITPY_PICOGAME_FRAMEBUFFER + +//| def refresh(self) -> Optional[list]: +//| """Repaint the regions changed by the scene's layers since the previous +//| refresh. Returns the bounding dirty rectangle as a list +//| ``[x1, y1, x2, y2]`` with exclusive ``x2``/``y2``, or `None` if nothing +//| changed. The returned list object is reused: read it before the next +//| ``refresh()`` call.""" +//| ... +//| +//| +static mp_obj_t picogame_scene_refresh(mp_obj_t self_in) { + picogame_scene_obj_t *self = MP_OBJ_TO_PTR(self_in); + + #if CIRCUITPY_PICOGAME_FRAMEBUFFER + if (self->fb_target) { + return scene_refresh_fb(self); + } + #endif + + // Resolve the underlying busdisplay from either backend. + busdisplay_busdisplay_obj_t *bd; + #if CIRCUITPY_PICOGAME_FAST_DISPLAY + picogame_display_obj_t *disp = NULL; + if (self->fast) { + disp = MP_OBJ_TO_PTR(self->display); + bd = disp->display; + } else + #endif + { + bd = MP_OBJ_TO_PTR(self->display); + } + int w = bd->core.width; + int h = bd->core.height; + + picogame_rect_t rects[PICOGAME_MAX_DIRTY_RECTS]; + int nr = scene_collect_dirty_rects(self, w, h, rects); + if (nr == 0) { + return mp_const_none; + } + + mp_buffer_info_t a, b; + mp_get_buffer_raise(self->buf_a, &a, MP_BUFFER_WRITE); + mp_get_buffer_raise(self->buf_b, &b, MP_BUFFER_WRITE); + #if CIRCUITPY_PICOGAME_FAST_DISPLAY + size_t buf_pixels = (a.len < b.len ? a.len : b.len) / 2; // fast path double-buffers + #endif + + // Render each dirty rect independently; return their bounding union (kept for + // the existing "dirty WxH" debug prints). + // Snapshots are already advanced; stay in the needs-full-repaint state until the + // render completes, so a BaseException mid-render (Ctrl-C in a StripDraw) leaves a + // scene whose NEXT refresh repaints everything instead of keeping a torn frame. + self->cleared = false; + for (int i = 0; i < nr; i++) { + #if CIRCUITPY_PICOGAME_FAST_DISPLAY + if (self->fast) { + common_hal_picogame_display_render(disp, self->items, self->kinds, self->count, + (uint16_t *)a.buf, (uint16_t *)b.buf, buf_pixels, + rects[i].x1, rects[i].y1, rects[i].x2, rects[i].y2, + self->background, self->ox, self->oy); + } else + #endif + { + // Portable single-buffer bus.send path (any CircuitPython port). + picogame_render_region(bd, self->items, self->kinds, self->count, + (uint16_t *)a.buf, a.len / 2, + rects[i].x1, rects[i].y1, rects[i].x2, rects[i].y2, + self->background, self->ox, self->oy); + } + } + self->cleared = true; + + return scene_store_dirty(self, rects, nr, w, h); +} +static MP_DEFINE_CONST_FUN_OBJ_1(picogame_scene_refresh_obj, picogame_scene_refresh); + +static const mp_rom_map_elem_t picogame_scene_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_add), MP_ROM_PTR(&picogame_scene_add_obj) }, + { MP_ROM_QSTR(MP_QSTR_add_all), MP_ROM_PTR(&picogame_scene_add_all_obj) }, + { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&picogame_scene_remove_obj) }, + { MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&picogame_scene_refresh_obj) }, + { MP_ROM_QSTR(MP_QSTR_invalidate), MP_ROM_PTR(&picogame_scene_invalidate_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_view), MP_ROM_PTR(&picogame_scene_set_view_obj) }, + { MP_ROM_QSTR(MP_QSTR_view), MP_ROM_PTR(&picogame_scene_view_obj) }, + { MP_ROM_QSTR(MP_QSTR_display), MP_ROM_PTR(&picogame_scene_display_obj) }, +}; +static MP_DEFINE_CONST_DICT(picogame_scene_locals_dict, picogame_scene_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + picogame_scene_type, + MP_QSTR_Scene, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, picogame_scene_make_new, + locals_dict, &picogame_scene_locals_dict + ); diff --git a/shared-bindings/picogame/Scene.h b/shared-bindings/picogame/Scene.h new file mode 100644 index 00000000000..733968b06ca --- /dev/null +++ b/shared-bindings/picogame/Scene.h @@ -0,0 +1,11 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-module/picogame/Scene.h" + +extern const mp_obj_type_t picogame_scene_type; diff --git a/shared-bindings/picogame/Sprite.c b/shared-bindings/picogame/Sprite.c new file mode 100644 index 00000000000..37f13775681 --- /dev/null +++ b/shared-bindings/picogame/Sprite.c @@ -0,0 +1,615 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT +// +// picogame.Sprite: a positioned, animatable instance of a Bitmap. + +#include "py/runtime.h" +#include "shared-module/picogame/pg_compat.h" +#include "shared-bindings/busdisplay/BusDisplay.h" +#include "shared-bindings/picogame/__init__.h" +#include "shared-bindings/picogame/Bitmap.h" +#include "shared-bindings/picogame/Sprite.h" +#if CIRCUITPY_PICOGAME_FAST_DISPLAY +#include "shared-bindings/picogame/Display.h" // fast DMA backend; absent on portable ports +#include "common-hal/picogame/Display.h" // its struct (pg_get_display unwraps the wrapper) +#endif +#include "shared-bindings/picogame/Scene.h" +#include "shared-bindings/picogame/Tilemap.h" +#include "shared-bindings/picogame/Particles.h" +#include "shared-bindings/picogame/Canvas.h" +#include "shared-bindings/picogame/Framebuffer.h" +#include "shared-module/picogame/__init__.h" +#include "shared-module/picogame/Bitmap.h" +#include "shared-module/picogame/Sprite.h" + +// --------------------------------------------------------------------------- +// Sprite +// --------------------------------------------------------------------------- + +// Int pixel/scale -> 24.8 fixed-point. Shift through unsigned so a wild coordinate (e.g. 100_000_000, +// in 32-bit mp_int range but not after <<8) wraps modularly instead of hitting signed-overflow UB. +// Costs nothing over a plain shift; for any real on-screen coordinate the result is identical. +static int32_t pg_int_to_fp8(mp_int_t v) { + return (int32_t)((uint32_t)v << 8); +} + +//| class Sprite: +//| """A positioned, animatable instance of a :py:class:`Bitmap`.""" +//| +//| def __init__( +//| self, +//| bitmap: Bitmap, +//| x: int = 0, +//| y: int = 0, +//| *, +//| frame: int = 0, +//| visible: bool = True, +//| flip_x: bool = False, +//| flip_y: bool = False, +//| ) -> None: +//| """Place ``bitmap`` with its top-left corner at ``(x, y)`` in scene +//| coordinates, showing the frame selected by ``frame``. Scene coordinates +//| follow the view offset, so the sprite moves with a scrolling world. +//| +//| ``visible=False`` creates the sprite without drawing it, for example to +//| pre-allocate a pool of sprites up front. ``flip_x`` and ``flip_y`` mirror +//| the frame at draw time.""" +//| ... +//| +static mp_obj_t picogame_sprite_make_new(const mp_obj_type_t *type, size_t n_args, + size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_bitmap, ARG_x, ARG_y, ARG_frame, ARG_visible, ARG_flip_x, ARG_flip_y }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_x, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_y, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_frame, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_visible, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + { MP_QSTR_flip_x, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_flip_y, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t bitmap_obj = mp_arg_validate_type(args[ARG_bitmap].u_obj, &picogame_bitmap_type, MP_QSTR_bitmap); + + picogame_sprite_obj_t *self = mp_obj_malloc(picogame_sprite_obj_t, type); + self->bitmap = MP_OBJ_TO_PTR(bitmap_obj); + self->x = pg_int_to_fp8(args[ARG_x].u_int); // pixel -> 24.8 fixed-point (overflow-clamped) + self->y = pg_int_to_fp8(args[ARG_y].u_int); + self->frame = args[ARG_frame].u_int; + self->flags = (args[ARG_visible].u_bool ? PICOGAME_SPR_VISIBLE : 0) + | (args[ARG_flip_x].u_bool ? PICOGAME_SPR_FLIP_X : 0) + | (args[ARG_flip_y].u_bool ? PICOGAME_SPR_FLIP_Y : 0); + self->anchor_x = 0; // default pivot = top-left (0, 0) + self->anchor_y = 0; + self->scale = 256; // 8.8 fixed-point: 256 = 1.0x (no scaling) + self->angle = 0; // no rotation -> axis-aligned fast path + self->flash_color = 0; + self->dither = 0; + self->data = mp_const_none; + self->seq = 0; // dirty-rect change counter (mp_obj_malloc zeroes anyway; explicit) + return MP_OBJ_FROM_PTR(self); +} + +static void set_flag(picogame_sprite_obj_t *self, uint8_t flag, bool on) { + if (on) { + self->flags |= flag; + } else { + self->flags &= ~flag; + } +} + +// shadow / flash / dither are mutually exclusive (one blit effect at a time); setting one ON +// clears the others, so "the last effect you set wins". Turning one OFF clears ONLY its own flag, +// so clearing an effect you never enabled (e.g. spr.flash = 0) can't wipe a different active one. +#define PICOGAME_SPR_FX_MASK (PICOGAME_SPR_SHADOW | PICOGAME_SPR_FLASH | PICOGAME_SPR_DITHER | PICOGAME_SPR_TINT) +static void set_effect(picogame_sprite_obj_t *self, uint8_t flag, bool on) { + if (on) { + self->flags = (uint8_t)((self->flags & ~PICOGAME_SPR_FX_MASK) | flag); + } else { + self->flags &= (uint8_t) ~flag; + } +} + +// Round a float to 24.8 fixed-point (shared by the position/scale/anchor setters so the soft-float +// round sequence is emitted once, not per call site). +static int32_t pg_round_fp8(mp_float_t f) { + return (int32_t)(f * 256 + (f >= 0 ? (mp_float_t)0.5 : (mp_float_t)-0.5)); +} + + +// Accept an int or float and store as 24.8 fixed-point (rounded). Integer fast path avoids +// software float on RP2040 - game code sets x/y (via move/setters) every frame, usually with ints. +static int32_t obj_to_fp(mp_obj_t o) { + if (mp_obj_is_int(o)) { + return pg_int_to_fp8(mp_obj_get_int(o)); + } + mp_float_t f = mp_obj_get_float(o); + return pg_round_fp8(f); +} + +//| +//| frame: int +//| """Which frame of the bitmap's atlas to draw, starting at 0. Stepping this +//| animates the sprite.""" +//| visible: bool +//| """`False` hides the sprite. It stays in the scene and the area under it +//| repaints.""" +//| flip_x: bool +//| """Mirror the frame horizontally at draw time.""" +//| flip_y: bool +//| """Mirror the frame vertically at draw time.""" +//| x: int +//| """Horizontal pixel position in scene coordinates. Setting accepts a float +//| for sub-pixel placement; reading returns the floored pixel.""" +//| y: int +//| """Vertical pixel position in scene coordinates. Setting accepts a float +//| for sub-pixel placement; reading returns the floored pixel.""" +//| fx: float +//| """Horizontal sub-pixel position, for example ``sprite.fx += 2.4``.""" +//| fy: float +//| """Vertical sub-pixel position, for example ``sprite.fy += 2.4``.""" +// FLASH NOTE (property objects vs. a single `attr` handler): Sprite (and the other property-dense +// types) expose each attribute as its own getter/setter + MP_PROPERTY object below - the standard +// CircuitPython shared-bindings idiom (~423 such uses across CP). Collapsing these onto ONE `attr` +// load/store function per type (a qstr switch, like py/objcomplex.c's complex_attr for .real/.imag) +// would save ~3-4 KB of flash across the whole binding layer (Sprite alone ~1.5-2.5 KB). It is a +// valid MicroPython mechanism and would NOT break the .pyi stubs (those are generated from the //| +// comments, not the C property objects). We deliberately KEEP property objects: they are the +// idiomatic, most readable shared-bindings form and keep every type's definition uniform. Revisit +// only if flash becomes critical (then convert Sprite first - the densest cluster - not every type). +static mp_obj_t sprite_get_x(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(((picogame_sprite_obj_t *)MP_OBJ_TO_PTR(self_in))->x >> 8); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sprite_get_x_obj, sprite_get_x); +static mp_obj_t sprite_set_x(mp_obj_t self_in, mp_obj_t v) { + ((picogame_sprite_obj_t *)MP_OBJ_TO_PTR(self_in))->x = obj_to_fp(v); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sprite_set_x_obj, sprite_set_x); +MP_PROPERTY_GETSET(sprite_x_obj, (mp_obj_t)&sprite_get_x_obj, (mp_obj_t)&sprite_set_x_obj); + +static mp_obj_t sprite_get_y(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(((picogame_sprite_obj_t *)MP_OBJ_TO_PTR(self_in))->y >> 8); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sprite_get_y_obj, sprite_get_y); +static mp_obj_t sprite_set_y(mp_obj_t self_in, mp_obj_t v) { + ((picogame_sprite_obj_t *)MP_OBJ_TO_PTR(self_in))->y = obj_to_fp(v); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sprite_set_y_obj, sprite_set_y); +MP_PROPERTY_GETSET(sprite_y_obj, (mp_obj_t)&sprite_get_y_obj, (mp_obj_t)&sprite_set_y_obj); + +static mp_obj_t sprite_get_fx(mp_obj_t self_in) { + return mp_obj_new_float(((picogame_sprite_obj_t *)MP_OBJ_TO_PTR(self_in))->x * (mp_float_t)(1.0 / 256.0)); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sprite_get_fx_obj, sprite_get_fx); +// set_fx is byte-identical to set_x (both store obj_to_fp(v) into ->x) -> reuse set_x's fun obj. +MP_PROPERTY_GETSET(sprite_fx_obj, (mp_obj_t)&sprite_get_fx_obj, (mp_obj_t)&sprite_set_x_obj); + +static mp_obj_t sprite_get_fy(mp_obj_t self_in) { + return mp_obj_new_float(((picogame_sprite_obj_t *)MP_OBJ_TO_PTR(self_in))->y * (mp_float_t)(1.0 / 256.0)); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sprite_get_fy_obj, sprite_get_fy); +// set_fy is byte-identical to set_y -> reuse set_y's fun obj. +MP_PROPERTY_GETSET(sprite_fy_obj, (mp_obj_t)&sprite_get_fy_obj, (mp_obj_t)&sprite_set_y_obj); + +static mp_obj_t sprite_get_frame(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(((picogame_sprite_obj_t *)MP_OBJ_TO_PTR(self_in))->frame); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sprite_get_frame_obj, sprite_get_frame); +static mp_obj_t sprite_set_frame(mp_obj_t self_in, mp_obj_t v) { + ((picogame_sprite_obj_t *)MP_OBJ_TO_PTR(self_in))->frame = mp_obj_get_int(v); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sprite_set_frame_obj, sprite_set_frame); +MP_PROPERTY_GETSET(sprite_frame_obj, (mp_obj_t)&sprite_get_frame_obj, (mp_obj_t)&sprite_set_frame_obj); + +static mp_obj_t sprite_get_visible(mp_obj_t self_in) { + return mp_obj_new_bool((((picogame_sprite_obj_t *)MP_OBJ_TO_PTR(self_in))->flags & PICOGAME_SPR_VISIBLE) != 0); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sprite_get_visible_obj, sprite_get_visible); +static mp_obj_t sprite_set_visible(mp_obj_t self_in, mp_obj_t v) { + set_flag(MP_OBJ_TO_PTR(self_in), PICOGAME_SPR_VISIBLE, mp_obj_is_true(v)); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sprite_set_visible_obj, sprite_set_visible); +MP_PROPERTY_GETSET(sprite_visible_obj, (mp_obj_t)&sprite_get_visible_obj, (mp_obj_t)&sprite_set_visible_obj); + +static mp_obj_t sprite_get_flip_x(mp_obj_t self_in) { + return mp_obj_new_bool((((picogame_sprite_obj_t *)MP_OBJ_TO_PTR(self_in))->flags & PICOGAME_SPR_FLIP_X) != 0); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sprite_get_flip_x_obj, sprite_get_flip_x); +static mp_obj_t sprite_set_flip_x(mp_obj_t self_in, mp_obj_t v) { + set_flag(MP_OBJ_TO_PTR(self_in), PICOGAME_SPR_FLIP_X, mp_obj_is_true(v)); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sprite_set_flip_x_obj, sprite_set_flip_x); +MP_PROPERTY_GETSET(sprite_flip_x_obj, (mp_obj_t)&sprite_get_flip_x_obj, (mp_obj_t)&sprite_set_flip_x_obj); + +static mp_obj_t sprite_get_flip_y(mp_obj_t self_in) { + return mp_obj_new_bool((((picogame_sprite_obj_t *)MP_OBJ_TO_PTR(self_in))->flags & PICOGAME_SPR_FLIP_Y) != 0); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sprite_get_flip_y_obj, sprite_get_flip_y); +static mp_obj_t sprite_set_flip_y(mp_obj_t self_in, mp_obj_t v) { + set_flag(MP_OBJ_TO_PTR(self_in), PICOGAME_SPR_FLIP_Y, mp_obj_is_true(v)); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sprite_set_flip_y_obj, sprite_set_flip_y); +MP_PROPERTY_GETSET(sprite_flip_y_obj, (mp_obj_t)&sprite_get_flip_y_obj, (mp_obj_t)&sprite_set_flip_y_obj); + +//| scale: float +//| """Uniform draw scale using nearest-neighbor sampling. ``1.0`` is native size; +//| fractional values are allowed. The anchor point stays put.""" +static mp_obj_t sprite_get_scale(mp_obj_t self_in) { + return mp_obj_new_float(((picogame_sprite_obj_t *)MP_OBJ_TO_PTR(self_in))->scale * (mp_float_t)(1.0 / 256.0)); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sprite_get_scale_obj, sprite_get_scale); +static mp_obj_t sprite_set_scale(mp_obj_t self_in, mp_obj_t v) { + int q; + if (mp_obj_is_int(v)) { // int fast path (no software float on RP2040) + q = pg_int_to_fp8(mp_obj_get_int(v)); // overflow-clamped <<8 + } else { + mp_float_t f = mp_obj_get_float(v); + q = pg_round_fp8(f); + } + if (q < 1) { + q = 1; + } + if (q > 65535) { + q = 65535; + } + picogame_sprite_obj_t *self = MP_OBJ_TO_PTR(self_in); + self->scale = (uint16_t)q; + self->xf_valid = 0; // affine cache depends on scale + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sprite_set_scale_obj, sprite_set_scale); +MP_PROPERTY_GETSET(sprite_scale_obj, (mp_obj_t)&sprite_get_scale_obj, (mp_obj_t)&sprite_set_scale_obj); + +//| angle: float +//| """Rotation in degrees about the anchor; ``0`` is unrotated. Values are stored +//| as whole degrees. Rotation uses nearest-neighbor sampling.""" +static mp_obj_t sprite_get_angle(mp_obj_t self_in) { + return mp_obj_new_float((mp_float_t)((picogame_sprite_obj_t *)MP_OBJ_TO_PTR(self_in))->angle); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sprite_get_angle_obj, sprite_get_angle); +static mp_obj_t sprite_set_angle(mp_obj_t self_in, mp_obj_t v) { + int a; + if (mp_obj_is_int(v)) { // int fast path (no software float on RP2040) + a = mp_obj_get_int(v); + } else { + mp_float_t f = mp_obj_get_float(v); + a = (int)(f + (f >= 0 ? (mp_float_t)0.5 : (mp_float_t)-0.5)); + } + a %= 360; + picogame_sprite_obj_t *self = MP_OBJ_TO_PTR(self_in); + self->angle = (int16_t)a; + self->xf_valid = 0; // affine cache depends on angle + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sprite_set_angle_obj, sprite_set_angle); +MP_PROPERTY_GETSET(sprite_angle_obj, (mp_obj_t)&sprite_get_angle_obj, (mp_obj_t)&sprite_set_angle_obj); + +//| shadow: bool +//| """Draw opaque pixels by darkening the destination instead of writing color, +//| producing a shadow silhouette or a dimming overlay. Mutually exclusive with +//| `flash`, `dither` and `tint`.""" +static mp_obj_t sprite_get_shadow(mp_obj_t self_in) { + return mp_obj_new_bool((((picogame_sprite_obj_t *)MP_OBJ_TO_PTR(self_in))->flags & PICOGAME_SPR_SHADOW) != 0); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sprite_get_shadow_obj, sprite_get_shadow); +static mp_obj_t sprite_set_shadow(mp_obj_t self_in, mp_obj_t v) { + set_effect(MP_OBJ_TO_PTR(self_in), PICOGAME_SPR_SHADOW, mp_obj_is_true(v)); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sprite_set_shadow_obj, sprite_set_shadow); +MP_PROPERTY_GETSET(sprite_shadow_obj, (mp_obj_t)&sprite_get_shadow_obj, (mp_obj_t)&sprite_set_shadow_obj); + +//| flash: int +//| """Draw opaque pixels as one solid color instead of their own, for example as +//| a brief hit flash. Set to a color from :py:func:`rgb565` to enable, ``0`` to +//| disable. Because ``0`` disables the effect, pure black cannot be the flash +//| color; use a near-black color instead. Mutually exclusive with `shadow`, +//| `dither` and `tint`.""" +static mp_obj_t sprite_get_flash(mp_obj_t self_in) { + picogame_sprite_obj_t *s = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT((s->flags & PICOGAME_SPR_FLASH) ? s->flash_color : 0); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sprite_get_flash_obj, sprite_get_flash); +static mp_obj_t sprite_set_flash(mp_obj_t self_in, mp_obj_t v) { + picogame_sprite_obj_t *s = MP_OBJ_TO_PTR(self_in); + // Falsy (None / False / 0) turns flash OFF. A non-zero colour enables it. (You can't flash + // pure black - use a near-black colour if you ever need that; off is the common case.) + if (!mp_obj_is_true(v)) { + set_effect(s, PICOGAME_SPR_FLASH, false); + } else { + s->flash_color = (uint16_t)mp_obj_get_int(v); + set_effect(s, PICOGAME_SPR_FLASH, true); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sprite_set_flash_obj, sprite_set_flash); +MP_PROPERTY_GETSET(sprite_flash_obj, (mp_obj_t)&sprite_get_flash_obj, (mp_obj_t)&sprite_set_flash_obj); + +//| dither: int +//| """Approximate transparency with an ordered (Bayer) dither pattern; there is +//| no alpha blending. ``0`` is opaque (off), ``8`` is about half transparent and +//| ``16`` is invisible. Mutually exclusive with `shadow`, `flash` and `tint`.""" +static mp_obj_t sprite_get_dither(mp_obj_t self_in) { + picogame_sprite_obj_t *s = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT((s->flags & PICOGAME_SPR_DITHER) ? s->dither : 0); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sprite_get_dither_obj, sprite_get_dither); +static mp_obj_t sprite_set_dither(mp_obj_t self_in, mp_obj_t v) { + picogame_sprite_obj_t *s = MP_OBJ_TO_PTR(self_in); + int lv = mp_obj_get_int(v); + if (lv < 0) { + lv = 0; + } + if (lv > 16) { + lv = 16; + } + s->dither = (uint8_t)lv; + set_effect(s, PICOGAME_SPR_DITHER, lv > 0); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sprite_set_dither_obj, sprite_set_dither); +MP_PROPERTY_GETSET(sprite_dither_obj, (mp_obj_t)&sprite_get_dither_obj, (mp_obj_t)&sprite_set_dither_obj); + +//| tint: int +//| """Multiply opaque pixels by a color from :py:func:`rgb565`, preserving the +//| sprite's shading (unlike `flash`, which replaces it). ``0`` disables, so pure +//| black cannot be the tint color. Mutually exclusive with `shadow`, `flash` +//| and `dither`.""" +static mp_obj_t sprite_get_tint(mp_obj_t self_in) { + picogame_sprite_obj_t *s = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT((s->flags & PICOGAME_SPR_TINT) ? s->flash_color : 0); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sprite_get_tint_obj, sprite_get_tint); +static mp_obj_t sprite_set_tint(mp_obj_t self_in, mp_obj_t v) { + picogame_sprite_obj_t *s = MP_OBJ_TO_PTR(self_in); + if (!mp_obj_is_true(v)) { + set_effect(s, PICOGAME_SPR_TINT, false); + } else { + s->flash_color = (uint16_t)mp_obj_get_int(v); // shared colour field with flash + set_effect(s, PICOGAME_SPR_TINT, true); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sprite_set_tint_obj, sprite_set_tint); +MP_PROPERTY_GETSET(sprite_tint_obj, (mp_obj_t)&sprite_get_tint_obj, (mp_obj_t)&sprite_set_tint_obj); + +//| transpose: bool +//| """Transpose the frame by swapping its x and y axes. Combined with `flip_x` +//| and `flip_y` this yields all 8 orthogonal orientations. Applies only at +//| ``scale == 1.0`` and ``angle == 0``; for rotation combined with scaling use +//| `angle`. The drawn footprint swaps width and height.""" +static mp_obj_t sprite_get_transpose(mp_obj_t self_in) { + return mp_obj_new_bool((((picogame_sprite_obj_t *)MP_OBJ_TO_PTR(self_in))->flags & PICOGAME_SPR_TRANSPOSE) != 0); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sprite_get_transpose_obj, sprite_get_transpose); +static mp_obj_t sprite_set_transpose(mp_obj_t self_in, mp_obj_t v) { + set_flag(MP_OBJ_TO_PTR(self_in), PICOGAME_SPR_TRANSPOSE, mp_obj_is_true(v)); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sprite_set_transpose_obj, sprite_set_transpose); +MP_PROPERTY_GETSET(sprite_transpose_obj, (mp_obj_t)&sprite_get_transpose_obj, (mp_obj_t)&sprite_set_transpose_obj); + +//| data: Any +//| """Arbitrary per-sprite user payload for game state (default None).""" +static mp_obj_t sprite_get_data(mp_obj_t self_in) { + return ((picogame_sprite_obj_t *)MP_OBJ_TO_PTR(self_in))->data; +} +static MP_DEFINE_CONST_FUN_OBJ_1(sprite_get_data_obj, sprite_get_data); +static mp_obj_t sprite_set_data(mp_obj_t self_in, mp_obj_t v) { + ((picogame_sprite_obj_t *)MP_OBJ_TO_PTR(self_in))->data = v; + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sprite_set_data_obj, sprite_set_data); +MP_PROPERTY_GETSET(sprite_data_obj, (mp_obj_t)&sprite_get_data_obj, (mp_obj_t)&sprite_set_data_obj); + +//| bitmap: Bitmap +//| """The sprite's source bitmap. Assigning a new one swaps the graphics and may +//| change the sprite's size; the scene repaints both the old and new bounds on +//| the next refresh.""" +static mp_obj_t sprite_get_bitmap(mp_obj_t self_in) { + picogame_sprite_obj_t *self = MP_OBJ_TO_PTR(self_in); + return self->bitmap != NULL ? MP_OBJ_FROM_PTR(self->bitmap) : mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(sprite_get_bitmap_obj, sprite_get_bitmap); +static mp_obj_t sprite_set_bitmap(mp_obj_t self_in, mp_obj_t v) { + picogame_sprite_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t bm = mp_arg_validate_type(v, &picogame_bitmap_type, MP_QSTR_bitmap); + self->bitmap = MP_OBJ_TO_PTR(bm); + self->xf_valid = 0; // affine cache depends on bitmap dims + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sprite_set_bitmap_obj, sprite_set_bitmap); +MP_PROPERTY_GETSET(sprite_bitmap_obj, (mp_obj_t)&sprite_get_bitmap_obj, (mp_obj_t)&sprite_set_bitmap_obj); + +//| anchor: Tuple[float, float] +//| """Pivot as fractions of the bitmap size: ``(0, 0)`` = top-left (default), +//| ``(0.5, 0.5)`` = center, ``(0.5, 1.0)`` = bottom-center. ``x``/``y`` then +//| refer to this point, so rotating frames or swapping to a different size +//| stays aligned. Stored in 1/256 steps.""" +//| +static mp_obj_t sprite_get_anchor(mp_obj_t self_in) { + picogame_sprite_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t t[2] = { + mp_obj_new_float(self->anchor_x * (mp_float_t)(1.0 / 256.0)), + mp_obj_new_float(self->anchor_y * (mp_float_t)(1.0 / 256.0)), + }; + return mp_obj_new_tuple(2, t); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sprite_get_anchor_obj, sprite_get_anchor); +static int anchor_to_fp(mp_obj_t o) { + mp_float_t f = mp_obj_get_float(o); + int v = pg_round_fp8(f); + return v < 0 ? 0 : (v > 256 ? 256 : v); +} +static mp_obj_t sprite_set_anchor(mp_obj_t self_in, mp_obj_t v) { + picogame_sprite_obj_t *self = MP_OBJ_TO_PTR(self_in); + size_t len; + mp_obj_t *items; + mp_obj_get_array(v, &len, &items); + mp_arg_validate_length(len, 2, MP_QSTR_anchor); + self->anchor_x = anchor_to_fp(items[0]); + self->anchor_y = anchor_to_fp(items[1]); + self->xf_valid = 0; // affine cache depends on the pivot + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sprite_set_anchor_obj, sprite_set_anchor); +MP_PROPERTY_GETSET(sprite_anchor_obj, (mp_obj_t)&sprite_get_anchor_obj, (mp_obj_t)&sprite_set_anchor_obj); + +//| def move(self, x: float, y: float) -> None: +//| """Set the sprite position. Accepts floats for sub-pixel placement.""" +//| ... +//| +static mp_obj_t sprite_move(mp_obj_t self_in, mp_obj_t x_in, mp_obj_t y_in) { + picogame_sprite_obj_t *self = MP_OBJ_TO_PTR(self_in); + self->x = obj_to_fp(x_in); + self->y = obj_to_fp(y_in); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_3(sprite_move_obj, sprite_move); + +//| def touch(self) -> None: +//| """Force this sprite to repaint on the next :py:meth:`Scene.refresh` even +//| though none of its tracked properties (position, frame, scale, angle, +//| bitmap) changed. Call it after mutating the bitmap's backing buffer in +//| place, which the dirty-region tracking cannot otherwise detect.""" +//| ... +//| +static mp_obj_t sprite_touch(mp_obj_t self_in) { + ((picogame_sprite_obj_t *)MP_OBJ_TO_PTR(self_in))->seq++; + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(sprite_touch_obj, sprite_touch); + +// Fill (x1,y1,x2,y2) from a Sprite (its drawn aabb), a (x,y) point, or a (x1,y1,x2,y2) rect. +static void pg_obj_to_box(mp_obj_t o, int *x1, int *y1, int *x2, int *y2) { + if (mp_obj_is_type(o, &picogame_sprite_type)) { + picogame_sprite_aabb(MP_OBJ_TO_PTR(o), x1, y1, x2, y2); + return; + } + if (mp_obj_is_type(o, &mp_type_tuple) || mp_obj_is_type(o, &mp_type_list)) { + size_t len; + mp_obj_t *items; + mp_obj_get_array(o, &len, &items); + if (len == 2) { // a point -> a zero-size box + *x1 = *x2 = mp_obj_get_int(items[0]); + *y1 = *y2 = mp_obj_get_int(items[1]); + return; + } + if (len == 4) { // a rect + *x1 = mp_obj_get_int(items[0]); + *y1 = mp_obj_get_int(items[1]); + *x2 = mp_obj_get_int(items[2]); + *y2 = mp_obj_get_int(items[3]); + return; + } + } + mp_raise_msg_varg(&mp_type_TypeError, MP_ERROR_TEXT("%q must be of type %q or %q, not %q"), + MP_QSTR_other, MP_QSTR_Sprite, MP_QSTR_tuple, mp_obj_get_type(o)->name); +} + +//| def overlaps( +//| self, +//| other: Union[Sprite, Tuple[int, int], Tuple[int, int, int, int]], +//| inset: int = 0, +//| ) -> bool: +//| """Return `True` if this sprite's drawn rectangle overlaps ``other``. Bounds +//| are inclusive, so touching edges count as an overlap. ``other`` may be +//| another `Sprite`, a point ``(x, y)`` or a rectangle ``(x1, y1, x2, y2)``. +//| The rectangle accounts for anchor, scale and rotation. ``inset`` shrinks +//| this sprite's rectangle by that many pixels on each side.""" +//| ... +//| +static mp_obj_t sprite_overlaps(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_other, ARG_inset }; + static const mp_arg_t allowed[] = { + { MP_QSTR_other, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_inset, MP_ARG_INT, {.u_int = 0} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed), allowed, args); + int ax1, ay1, ax2, ay2, bx1, by1, bx2, by2; + picogame_sprite_aabb(MP_OBJ_TO_PTR(pos_args[0]), &ax1, &ay1, &ax2, &ay2); + pg_obj_to_box(args[ARG_other].u_obj, &bx1, &by1, &bx2, &by2); + int in = args[ARG_inset].u_int; // inset shrinks the CALLER's box (kw or positional) + bool hit = ((ax1 + in) <= bx2) && ((ax2 - in) >= bx1) && + ((ay1 + in) <= by2) && ((ay2 - in) >= by1); + return mp_obj_new_bool(hit); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(sprite_overlaps_obj, 2, sprite_overlaps); + +//| def near(self, other: Union[Sprite, Tuple[int, int]], r: int) -> bool: +//| """Return `True` when the distance between centers is less than ``r`` +//| pixels. ``other`` may be a `Sprite` or a point ``(x, y)``. Centers come +//| from the drawn rectangle, so the test accounts for the anchor.""" +//| ... +//| +//| +static mp_obj_t sprite_near(mp_obj_t self_in, mp_obj_t other_in, mp_obj_t r_in) { + int ax1, ay1, ax2, ay2; + picogame_sprite_aabb(MP_OBJ_TO_PTR(self_in), &ax1, &ay1, &ax2, &ay2); + int acx = (ax1 + ax2) / 2, acy = (ay1 + ay2) / 2, bcx, bcy; + if (mp_obj_is_type(other_in, &picogame_sprite_type)) { + int bx1, by1, bx2, by2; + picogame_sprite_aabb(MP_OBJ_TO_PTR(other_in), &bx1, &by1, &bx2, &by2); + bcx = (bx1 + bx2) / 2; + bcy = (by1 + by2) / 2; + } else { + size_t len; + mp_obj_t *items; + mp_obj_get_array(other_in, &len, &items); + if (len != 2) { + mp_raise_msg_varg(&mp_type_TypeError, MP_ERROR_TEXT("%q must be of type %q or %q, not %q"), + MP_QSTR_other, MP_QSTR_Sprite, MP_QSTR_tuple, mp_obj_get_type(other_in)->name); + } + bcx = mp_obj_get_int(items[0]); + bcy = mp_obj_get_int(items[1]); + } + mp_int_t r = mp_obj_get_int(r_in), dx = acx - bcx, dy = acy - bcy; + return mp_obj_new_bool(dx * dx + dy * dy < r * r); +} +static MP_DEFINE_CONST_FUN_OBJ_3(sprite_near_obj, sprite_near); + +static const mp_rom_map_elem_t picogame_sprite_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&sprite_x_obj) }, + { MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&sprite_y_obj) }, + { MP_ROM_QSTR(MP_QSTR_fx), MP_ROM_PTR(&sprite_fx_obj) }, + { MP_ROM_QSTR(MP_QSTR_fy), MP_ROM_PTR(&sprite_fy_obj) }, + { MP_ROM_QSTR(MP_QSTR_frame), MP_ROM_PTR(&sprite_frame_obj) }, + { MP_ROM_QSTR(MP_QSTR_visible), MP_ROM_PTR(&sprite_visible_obj) }, + { MP_ROM_QSTR(MP_QSTR_flip_x), MP_ROM_PTR(&sprite_flip_x_obj) }, + { MP_ROM_QSTR(MP_QSTR_flip_y), MP_ROM_PTR(&sprite_flip_y_obj) }, + { MP_ROM_QSTR(MP_QSTR_scale), MP_ROM_PTR(&sprite_scale_obj) }, + { MP_ROM_QSTR(MP_QSTR_angle), MP_ROM_PTR(&sprite_angle_obj) }, + { MP_ROM_QSTR(MP_QSTR_shadow), MP_ROM_PTR(&sprite_shadow_obj) }, + { MP_ROM_QSTR(MP_QSTR_flash), MP_ROM_PTR(&sprite_flash_obj) }, + { MP_ROM_QSTR(MP_QSTR_dither), MP_ROM_PTR(&sprite_dither_obj) }, + { MP_ROM_QSTR(MP_QSTR_tint), MP_ROM_PTR(&sprite_tint_obj) }, + { MP_ROM_QSTR(MP_QSTR_transpose), MP_ROM_PTR(&sprite_transpose_obj) }, + { MP_ROM_QSTR(MP_QSTR_data), MP_ROM_PTR(&sprite_data_obj) }, + { MP_ROM_QSTR(MP_QSTR_bitmap), MP_ROM_PTR(&sprite_bitmap_obj) }, + { MP_ROM_QSTR(MP_QSTR_anchor), MP_ROM_PTR(&sprite_anchor_obj) }, + { MP_ROM_QSTR(MP_QSTR_overlaps), MP_ROM_PTR(&sprite_overlaps_obj) }, + { MP_ROM_QSTR(MP_QSTR_near), MP_ROM_PTR(&sprite_near_obj) }, + { MP_ROM_QSTR(MP_QSTR_move), MP_ROM_PTR(&sprite_move_obj) }, + { MP_ROM_QSTR(MP_QSTR_touch), MP_ROM_PTR(&sprite_touch_obj) }, +}; +static MP_DEFINE_CONST_DICT(picogame_sprite_locals_dict, picogame_sprite_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + picogame_sprite_type, + MP_QSTR_Sprite, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, picogame_sprite_make_new, + locals_dict, &picogame_sprite_locals_dict + ); diff --git a/shared-bindings/picogame/Sprite.h b/shared-bindings/picogame/Sprite.h new file mode 100644 index 00000000000..97c38c8b762 --- /dev/null +++ b/shared-bindings/picogame/Sprite.h @@ -0,0 +1,11 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-module/picogame/Sprite.h" + +extern const mp_obj_type_t picogame_sprite_type; diff --git a/shared-bindings/picogame/StripDraw.c b/shared-bindings/picogame/StripDraw.c new file mode 100644 index 00000000000..45bfc591e35 --- /dev/null +++ b/shared-bindings/picogame/StripDraw.c @@ -0,0 +1,241 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT +// +// picogame.StripDraw: an immediate-mode draw layer that holds no pixel buffer. + +#include "py/runtime.h" +#include "shared-module/picogame/pg_compat.h" +#include "shared-bindings/busdisplay/BusDisplay.h" +#include "shared-bindings/picogame/__init__.h" +#include "shared-bindings/picogame/Bitmap.h" +#include "shared-bindings/picogame/Sprite.h" +#if CIRCUITPY_PICOGAME_FAST_DISPLAY +#include "shared-bindings/picogame/Display.h" // fast DMA backend; absent on portable ports +#include "common-hal/picogame/Display.h" // its struct (pg_get_display unwraps the wrapper) +#endif +#include "shared-bindings/picogame/Scene.h" +#include "shared-bindings/picogame/Tilemap.h" +#include "shared-bindings/picogame/Particles.h" +#include "shared-bindings/picogame/Canvas.h" +#include "shared-bindings/picogame/Framebuffer.h" +#include "shared-bindings/picogame/StripDraw.h" +#include "shared-module/picogame/__init__.h" +#include "shared-module/picogame/Bitmap.h" +#include "shared-module/picogame/Sprite.h" + +// --------------------------------------------------------------------------- +// StripDraw (immediate-mode draw layer; struct in shared-module/picogame/__init__.h) +// --------------------------------------------------------------------------- + +//| class StripDraw: +//| """An immediate-mode draw layer that holds no pixel buffer. On each refresh, +//| for every render strip overlapping its rectangle, ``callback(view, vx, vy, +//| vw, vh)`` is called with a `Canvas` view of the live strip buffer, so the +//| callback draws primitives directly into the frame. StripDraw layers always +//| draw in screen coordinates and ignore the scene's view offset. +//| +//| ``(vx, vy)`` is the view origin in screen coordinates and ``(vw, vh)`` is +//| its size: to draw a screen point ``(sx, sy)``, draw at ``(sx - vx, sy - vy)`` +//| in ``view``. The view may span the full render-region width even when the +//| layer is narrower (the layer's rectangle only limits which rows are drawn), +//| so fill your own rectangle with :py:meth:`Canvas.fill_rect` rather than +//| ``view.clear()``, which fills the whole width. +//| +//| Moving or resizing the layer by assigning :py:attr:`x`, :py:attr:`y`, +//| :py:attr:`width` or :py:attr:`height` can leave the old area stale; call +//| :py:meth:`Scene.invalidate` afterwards for a clean repaint.""" +//| +//| def __init__( +//| self, +//| callback: Callable[[Canvas, int, int, int, int], None], +//| x: int = 0, +//| y: int = 0, +//| width: int = 0, +//| height: int = 0, +//| *, +//| always_dirty: bool = True, +//| ) -> None: +//| """:param callback: called for each overlapping render strip as +//| ``callback(view, vx, vy, vw, vh)`` +//| :param int x: left edge of the layer's screen rectangle +//| :param int y: top edge of the layer's screen rectangle +//| :param int width: rectangle width in pixels +//| :param int height: rectangle height in pixels +//| :param bool always_dirty: when `True` the layer redraws every refresh; +//| when `False`, call :py:meth:`invalidate` after its content changes""" +//| ... +//| +static mp_obj_t picogame_stripdraw_make_new(const mp_obj_type_t *type, size_t n_args, + size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_callback, ARG_x, ARG_y, ARG_width, ARG_height, ARG_always_dirty }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_callback, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_x, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_y, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_width, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_height, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_always_dirty, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + picogame_stripdraw_obj_t *self = mp_obj_malloc(picogame_stripdraw_obj_t, type); + self->callback = args[ARG_callback].u_obj; + self->x = args[ARG_x].u_int; + self->y = args[ARG_y].u_int; + self->w = args[ARG_width].u_int; + self->h = args[ARG_height].u_int; + self->faulted = false; + self->always_dirty = args[ARG_always_dirty].u_bool; + picogame_dirty_reset(&self->dx1); // render once on first refresh (even when always_dirty=False) + picogame_dirty_union(&self->dx1, self->x, self->y, self->x + self->w, self->y + self->h); + // A buffer-less Canvas reused as the per-strip drawing view: its `data` is + // repointed at the live strip each blit, so no surface RAM is allocated here. + picogame_canvas_obj_t *view = mp_obj_malloc(picogame_canvas_obj_t, &picogame_canvas_type); + view->data = NULL; + view->data_obj = MP_OBJ_NULL; + view->w = 0; + view->h = 0; + view->x = 0; + view->y = 0; + view->transparent = 0; + view->has_transparent = false; + picogame_canvas_dirty_reset(view); + self->view = MP_OBJ_FROM_PTR(view); + return MP_OBJ_FROM_PTR(self); +} + +//| +//| x: int +//| """Left edge of the layer's screen rectangle.""" +//| y: int +//| """Top edge of the layer's screen rectangle.""" +//| width: int +//| """Width of the layer's screen rectangle in pixels.""" +//| height: int +//| """Height of the layer's screen rectangle in pixels.""" +static mp_obj_t sd_get_x(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(((picogame_stripdraw_obj_t *)MP_OBJ_TO_PTR(self_in))->x); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sd_get_x_obj, sd_get_x); +static mp_obj_t sd_set_x(mp_obj_t self_in, mp_obj_t v) { + ((picogame_stripdraw_obj_t *)MP_OBJ_TO_PTR(self_in))->x = mp_obj_get_int(v); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sd_set_x_obj, sd_set_x); +MP_PROPERTY_GETSET(sd_x_obj, (mp_obj_t)&sd_get_x_obj, (mp_obj_t)&sd_set_x_obj); + +static mp_obj_t sd_get_y(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(((picogame_stripdraw_obj_t *)MP_OBJ_TO_PTR(self_in))->y); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sd_get_y_obj, sd_get_y); +static mp_obj_t sd_set_y(mp_obj_t self_in, mp_obj_t v) { + ((picogame_stripdraw_obj_t *)MP_OBJ_TO_PTR(self_in))->y = mp_obj_get_int(v); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sd_set_y_obj, sd_set_y); +MP_PROPERTY_GETSET(sd_y_obj, (mp_obj_t)&sd_get_y_obj, (mp_obj_t)&sd_set_y_obj); + +static mp_obj_t sd_get_width(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(((picogame_stripdraw_obj_t *)MP_OBJ_TO_PTR(self_in))->w); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sd_get_width_obj, sd_get_width); +static mp_obj_t sd_set_width(mp_obj_t self_in, mp_obj_t v) { + ((picogame_stripdraw_obj_t *)MP_OBJ_TO_PTR(self_in))->w = mp_obj_get_int(v); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sd_set_width_obj, sd_set_width); +MP_PROPERTY_GETSET(sd_width_obj, (mp_obj_t)&sd_get_width_obj, (mp_obj_t)&sd_set_width_obj); + +static mp_obj_t sd_get_height(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(((picogame_stripdraw_obj_t *)MP_OBJ_TO_PTR(self_in))->h); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sd_get_height_obj, sd_get_height); +static mp_obj_t sd_set_height(mp_obj_t self_in, mp_obj_t v) { + ((picogame_stripdraw_obj_t *)MP_OBJ_TO_PTR(self_in))->h = mp_obj_get_int(v); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sd_set_height_obj, sd_set_height); +MP_PROPERTY_GETSET(sd_height_obj, (mp_obj_t)&sd_get_height_obj, (mp_obj_t)&sd_set_height_obj); + +//| always_dirty: bool +//| """When `True` (the default) the rectangle repaints every frame, for animated +//| content. When `False` the layer renders once initially and then repaints only +//| after an :py:meth:`invalidate` call or when overlapped by another dirty +//| layer; call :py:meth:`invalidate` after each content change.""" +//| +static mp_obj_t sd_get_always_dirty(mp_obj_t self_in) { + return mp_obj_new_bool(((picogame_stripdraw_obj_t *)MP_OBJ_TO_PTR(self_in))->always_dirty); +} +static MP_DEFINE_CONST_FUN_OBJ_1(sd_get_always_dirty_obj, sd_get_always_dirty); +static mp_obj_t sd_set_always_dirty(mp_obj_t self_in, mp_obj_t v) { + ((picogame_stripdraw_obj_t *)MP_OBJ_TO_PTR(self_in))->always_dirty = mp_obj_is_true(v); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(sd_set_always_dirty_obj, sd_set_always_dirty); +MP_PROPERTY_GETSET(sd_always_dirty_obj, (mp_obj_t)&sd_get_always_dirty_obj, (mp_obj_t)&sd_set_always_dirty_obj); + +//| def invalidate(self, x: int = 0, y: int = 0, w: int = 0, h: int = 0) -> None: +//| """Mark the layer dirty so it repaints on the next refresh; only needed +//| when ``always_dirty`` is `False`. +//| +//| With no arguments the whole layer repaints. To repaint one region, pass +//| all four values as a rectangle in the view-local coordinates the draw +//| callback uses; passing only some of them raises :py:class:`ValueError`. +//| Repeated calls accumulate, and the rectangle is clamped to the layer.""" +//| ... +//| +//| +static mp_obj_t sd_invalidate(size_t n_args, const mp_obj_t *args) { + picogame_stripdraw_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (n_args != 1 && n_args != 5) { + // A partial rectangle is a bug, not a request for "everything". + mp_arg_error_invalid(MP_QSTR_rect); + } + if (n_args >= 5) { // (x, y, w, h) in view-local coords -> clamped scene rect + int x1 = self->x + mp_obj_get_int(args[1]); + int y1 = self->y + mp_obj_get_int(args[2]); + int x2 = x1 + mp_obj_get_int(args[3]); + int y2 = y1 + mp_obj_get_int(args[4]); + if (x1 < self->x) { + x1 = self->x; + } + if (y1 < self->y) { + y1 = self->y; + } + if (x2 > self->x + self->w) { + x2 = self->x + self->w; + } + if (y2 > self->y + self->h) { + y2 = self->y + self->h; + } + if (x2 > x1 && y2 > y1) { + picogame_dirty_union(&self->dx1, x1, y1, x2, y2); + } + } else { // whole layer + picogame_dirty_union(&self->dx1, self->x, self->y, self->x + self->w, self->y + self->h); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(sd_invalidate_obj, 1, 5, sd_invalidate); + +static const mp_rom_map_elem_t picogame_stripdraw_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&sd_x_obj) }, + { MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&sd_y_obj) }, + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&sd_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&sd_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_always_dirty), MP_ROM_PTR(&sd_always_dirty_obj) }, + { MP_ROM_QSTR(MP_QSTR_invalidate), MP_ROM_PTR(&sd_invalidate_obj) }, +}; +static MP_DEFINE_CONST_DICT(picogame_stripdraw_locals_dict, picogame_stripdraw_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + picogame_stripdraw_type, + MP_QSTR_StripDraw, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, picogame_stripdraw_make_new, + locals_dict, &picogame_stripdraw_locals_dict + ); diff --git a/shared-bindings/picogame/StripDraw.h b/shared-bindings/picogame/StripDraw.h new file mode 100644 index 00000000000..68c1506bacf --- /dev/null +++ b/shared-bindings/picogame/StripDraw.h @@ -0,0 +1,11 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" + +extern const mp_obj_type_t picogame_stripdraw_type; diff --git a/shared-bindings/picogame/Tilemap.c b/shared-bindings/picogame/Tilemap.c new file mode 100644 index 00000000000..955deef1b96 --- /dev/null +++ b/shared-bindings/picogame/Tilemap.c @@ -0,0 +1,247 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#include "py/runtime.h" +#include "shared-module/picogame/pg_compat.h" +#include "shared-bindings/picogame/Tilemap.h" +#include "shared-bindings/picogame/Bitmap.h" +#include "shared-module/picogame/Tilemap.h" + +//| class Tilemap: +//| """A grid of tile indices into a tileset `Bitmap`, where each bitmap frame +//| is one tile. Add it to a `Scene` as a background layer; setting tiles or +//| moving the map marks only the affected area dirty.""" +//| +//| def __init__(self, tileset: Bitmap, cols: int, rows: int) -> None: +//| """:param Bitmap tileset: the tile images, one frame per tile +//| :param int cols: map width in tiles +//| :param int rows: map height in tiles""" +//| ... +//| +static mp_obj_t picogame_tilemap_make_new(const mp_obj_type_t *type, size_t n_args, + size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_tileset, ARG_cols, ARG_rows }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_tileset, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_cols, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_rows, MP_ARG_REQUIRED | MP_ARG_INT }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + mp_obj_t tileset_obj = mp_arg_validate_type(args[ARG_tileset].u_obj, &picogame_bitmap_type, MP_QSTR_tileset); + // Cap cols/rows (not just floor): an unbounded cols*rows overflows the size_t map/orient + // allocation on a 32-bit port, desyncing alloc size from the index space (same family as the + // Bitmap width*frames guard). 1024 each -> product <= 1M, far below SIZE_MAX; oversize maps + // MemoryError at alloc (safe). + mp_int_t map_w = mp_arg_validate_int_range(args[ARG_cols].u_int, 1, 1024, MP_QSTR_cols); + mp_int_t map_h = mp_arg_validate_int_range(args[ARG_rows].u_int, 1, 1024, MP_QSTR_rows); + + mp_obj_t map_obj = mp_obj_new_bytearray_of_zeros((size_t)map_w * map_h); + mp_buffer_info_t mi; + mp_get_buffer_raise(map_obj, &mi, MP_BUFFER_RW); + + picogame_tilemap_obj_t *self = mp_obj_malloc(picogame_tilemap_obj_t, type); + self->tileset = MP_OBJ_TO_PTR(tileset_obj); + self->tileset_obj = tileset_obj; + self->map = mi.buf; + self->map_obj = map_obj; + self->orient = NULL; // orientation plane allocated lazily on first flipped/rotated tile + self->orient_obj = mp_const_none; + self->map_w = map_w; + self->map_h = map_h; + self->x = 0; + self->y = 0; + picogame_tilemap_dirty_reset(self); + return MP_OBJ_FROM_PTR(self); +} + +//| def get_tile(self, tx: int, ty: int) -> int: +//| """Return the tile index at (tx, ty). Out-of-range reads as 0.""" +//| ... +//| +static mp_obj_t picogame_tilemap_get_tile(mp_obj_t self_in, mp_obj_t tx_in, mp_obj_t ty_in) { + picogame_tilemap_obj_t *self = MP_OBJ_TO_PTR(self_in); + int tx = mp_obj_get_int(tx_in); + int ty = mp_obj_get_int(ty_in); + if (tx < 0 || ty < 0 || tx >= self->map_w || ty >= self->map_h) { + return MP_OBJ_NEW_SMALL_INT(0); + } + return MP_OBJ_NEW_SMALL_INT(self->map[(size_t)ty * self->map_w + tx]); +} +static MP_DEFINE_CONST_FUN_OBJ_3(picogame_tilemap_get_tile_obj, picogame_tilemap_get_tile); + +//| def set_tile( +//| self, +//| tx: int, +//| ty: int, +//| value: int, +//| *, +//| flip_x: bool = False, +//| flip_y: bool = False, +//| transpose: bool = False, +//| ) -> None: +//| """Set the tile at ``(tx, ty)`` and mark it dirty. The keyword flags orient +//| the tile at draw time; together they yield all 8 orientations (4 rotations +//| times mirror), so one stored tile can serve as several. The flags can +//| represent the remap table emitted by the ``png2picogame.py --dedup`` +//| tool. Out-of-range writes are ignored.""" +//| ... +//| +static mp_obj_t picogame_tilemap_set_tile(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_tx, ARG_ty, ARG_value, ARG_flip_x, ARG_flip_y, ARG_transpose }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_tx, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_ty, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_value, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_flip_x, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_flip_y, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_transpose, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + picogame_tilemap_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + int tx = args[ARG_tx].u_int; + int ty = args[ARG_ty].u_int; + if (tx < 0 || ty < 0 || tx >= self->map_w || ty >= self->map_h) { + return mp_const_none; + } + size_t off = (size_t)ty * self->map_w + tx; + uint8_t v = args[ARG_value].u_int & 0xff; + uint8_t o = 0; + if (args[ARG_flip_x].u_bool) { + o |= 1; + } + if (args[ARG_flip_y].u_bool) { + o |= 2; + } + if (args[ARG_transpose].u_bool) { + o |= 4; + } + // Allocate the orientation plane lazily - only maps that actually use flips/rotation + // pay the RAM (1 byte/cell). + if (o != 0 && self->orient == NULL) { + mp_obj_t ob = mp_obj_new_bytearray_of_zeros((size_t)self->map_w * self->map_h); + mp_buffer_info_t oi; + mp_get_buffer_raise(ob, &oi, MP_BUFFER_RW); + self->orient = oi.buf; + self->orient_obj = ob; + } + uint8_t old_o = self->orient ? self->orient[off] : 0; + if (self->map[off] != v || old_o != o) { + self->map[off] = v; + if (self->orient) { + self->orient[off] = o; + } + int tw = self->tileset ? self->tileset->width : 0; + int th = self->tileset ? self->tileset->height : 0; + int sx = self->x + tx * tw; + int sy = self->y + ty * th; + picogame_tilemap_dirty_union(self, sx, sy, sx + tw, sy + th); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_KW(picogame_tilemap_set_tile_obj, 4, picogame_tilemap_set_tile); + +//| def move(self, x: int, y: int) -> None: +//| """Move the whole map to pixel (x, y).""" +//| ... +//| +static mp_obj_t picogame_tilemap_move(mp_obj_t self_in, mp_obj_t x_in, mp_obj_t y_in) { + picogame_tilemap_obj_t *self = MP_OBJ_TO_PTR(self_in); + int nx = mp_obj_get_int(x_in), ny = mp_obj_get_int(y_in); + if (nx == self->x && ny == self->y) { + return mp_const_none; // unchanged -> avoid a full-tilemap repaint + } + int ox1, oy1, ox2, oy2; + picogame_tilemap_extent(self, &ox1, &oy1, &ox2, &oy2); + self->x = nx; + self->y = ny; + int nx1, ny1, nx2, ny2; + picogame_tilemap_extent(self, &nx1, &ny1, &nx2, &ny2); + picogame_tilemap_dirty_union(self, ox1, oy1, ox2, oy2); + picogame_tilemap_dirty_union(self, nx1, ny1, nx2, ny2); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_3(picogame_tilemap_move_obj, picogame_tilemap_move); + +//| def fill(self, value: int) -> None: +//| """Set every tile to ``value``.""" +//| ... +//| +static mp_obj_t picogame_tilemap_fill(mp_obj_t self_in, mp_obj_t value_in) { + picogame_tilemap_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint8_t v = mp_obj_get_int(value_in) & 0xff; + size_t total = (size_t)self->map_w * self->map_h; + for (size_t i = 0; i < total; i++) { + self->map[i] = v; + if (self->orient) { + self->orient[i] = 0; // a plain fill clears any per-cell orientation + } + } + int x1, y1, x2, y2; + picogame_tilemap_extent(self, &x1, &y1, &x2, &y2); + picogame_tilemap_dirty_union(self, x1, y1, x2, y2); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(picogame_tilemap_fill_obj, picogame_tilemap_fill); + +//| x: int +//| """Horizontal position of the map's top-left corner. (read-only) +//| +//| Set with :py:meth:`move`.""" +//| y: int +//| """Vertical position of the map's top-left corner. (read-only) +//| +//| Set with :py:meth:`move`.""" +//| cols: int +//| """Map width in tiles. (read-only)""" +//| rows: int +//| """Map height in tiles. (read-only)""" +//| +//| +static mp_obj_t tilemap_get_x(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(((picogame_tilemap_obj_t *)MP_OBJ_TO_PTR(self_in))->x); +} +static MP_DEFINE_CONST_FUN_OBJ_1(tilemap_get_x_obj, tilemap_get_x); +MP_PROPERTY_GETTER(tilemap_x_obj, (mp_obj_t)&tilemap_get_x_obj); + +static mp_obj_t tilemap_get_y(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(((picogame_tilemap_obj_t *)MP_OBJ_TO_PTR(self_in))->y); +} +static MP_DEFINE_CONST_FUN_OBJ_1(tilemap_get_y_obj, tilemap_get_y); +MP_PROPERTY_GETTER(tilemap_y_obj, (mp_obj_t)&tilemap_get_y_obj); + +static mp_obj_t tilemap_get_cols(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(((picogame_tilemap_obj_t *)MP_OBJ_TO_PTR(self_in))->map_w); +} +static MP_DEFINE_CONST_FUN_OBJ_1(tilemap_get_cols_obj, tilemap_get_cols); +MP_PROPERTY_GETTER(tilemap_cols_obj, (mp_obj_t)&tilemap_get_cols_obj); + +static mp_obj_t tilemap_get_rows(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(((picogame_tilemap_obj_t *)MP_OBJ_TO_PTR(self_in))->map_h); +} +static MP_DEFINE_CONST_FUN_OBJ_1(tilemap_get_rows_obj, tilemap_get_rows); +MP_PROPERTY_GETTER(tilemap_rows_obj, (mp_obj_t)&tilemap_get_rows_obj); + +static const mp_rom_map_elem_t picogame_tilemap_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_get_tile), MP_ROM_PTR(&picogame_tilemap_get_tile_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_tile), MP_ROM_PTR(&picogame_tilemap_set_tile_obj) }, + { MP_ROM_QSTR(MP_QSTR_move), MP_ROM_PTR(&picogame_tilemap_move_obj) }, + { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&picogame_tilemap_fill_obj) }, + { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&tilemap_x_obj) }, + { MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&tilemap_y_obj) }, + { MP_ROM_QSTR(MP_QSTR_cols), MP_ROM_PTR(&tilemap_cols_obj) }, + { MP_ROM_QSTR(MP_QSTR_rows), MP_ROM_PTR(&tilemap_rows_obj) }, +}; +static MP_DEFINE_CONST_DICT(picogame_tilemap_locals_dict, picogame_tilemap_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + picogame_tilemap_type, + MP_QSTR_Tilemap, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, picogame_tilemap_make_new, + locals_dict, &picogame_tilemap_locals_dict + ); diff --git a/shared-bindings/picogame/Tilemap.h b/shared-bindings/picogame/Tilemap.h new file mode 100644 index 00000000000..9c1308f9eaa --- /dev/null +++ b/shared-bindings/picogame/Tilemap.h @@ -0,0 +1,11 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-module/picogame/Tilemap.h" + +extern const mp_obj_type_t picogame_tilemap_type; diff --git a/shared-bindings/picogame/Triangles.c b/shared-bindings/picogame/Triangles.c new file mode 100644 index 00000000000..4401a1b9c66 --- /dev/null +++ b/shared-bindings/picogame/Triangles.c @@ -0,0 +1,102 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT +// +// picogame.Triangles: a retained screen-space triangle batch rasterised in C. + +#include "py/runtime.h" +#include "shared-module/picogame/pg_compat.h" +#include "shared-bindings/busdisplay/BusDisplay.h" +#include "shared-bindings/picogame/__init__.h" +#include "shared-bindings/picogame/Bitmap.h" +#include "shared-bindings/picogame/Sprite.h" +#if CIRCUITPY_PICOGAME_FAST_DISPLAY +#include "shared-bindings/picogame/Display.h" // fast DMA backend; absent on portable ports +#include "common-hal/picogame/Display.h" // its struct (pg_get_display unwraps the wrapper) +#endif +#include "shared-bindings/picogame/Scene.h" +#include "shared-bindings/picogame/Tilemap.h" +#include "shared-bindings/picogame/Particles.h" +#include "shared-bindings/picogame/Canvas.h" +#include "shared-bindings/picogame/Framebuffer.h" +#include "shared-bindings/picogame/Triangles.h" +#include "shared-module/picogame/__init__.h" +#include "shared-module/picogame/Bitmap.h" +#include "shared-module/picogame/Sprite.h" + +//| class Triangles: +//| """A retained triangle batch drawn as a `Scene` layer. The batch is +//| rasterized directly into each render strip; no per-strip Python runs and no +//| pixel buffer is held. Triangles layers always draw in screen coordinates and +//| ignore the scene's view offset. +//| +//| For each frame: project points with :py:func:`project`, write triangles +//| into the buffers in back-to-front order, set :py:attr:`count`, then call +//| :py:meth:`Scene.refresh`.""" +//| +//| def __init__(self, verts: ReadableBuffer, colors: ReadableBuffer) -> None: +//| """:param ~circuitpython_typing.ReadableBuffer verts: caller-owned buffer of +//| ``int16`` values, six per triangle: x0, y0, x1, y1, x2, y2. Refill it in +//| place each frame. +//| :param ~circuitpython_typing.ReadableBuffer colors: caller-owned buffer of +//| ``uint16`` colors, one per triangle""" +//| ... +//| +static mp_obj_t picogame_triangles_make_new(const mp_obj_type_t *type, size_t n_args, + size_t n_kw, const mp_obj_t *all_args) { + mp_arg_check_num(n_args, n_kw, 2, 2, false); + picogame_triangles_obj_t *self = mp_obj_malloc(picogame_triangles_obj_t, type); + mp_buffer_info_t vi, ci; + mp_get_buffer_raise(all_args[0], &vi, MP_BUFFER_READ); + mp_get_buffer_raise(all_args[1], &ci, MP_BUFFER_READ); + self->verts_obj = all_args[0]; + self->colors_obj = all_args[1]; + self->verts = (const int16_t *)vi.buf; + self->colors = (const uint16_t *)ci.buf; + size_t cap_v = vi.len / 12; // 6 int16 = 12 bytes per triangle + size_t cap_c = ci.len >> 1; + self->cap = (uint16_t)(cap_v < cap_c ? cap_v : cap_c); + self->count = 0; + picogame_dirty_reset(&self->dx1); + return MP_OBJ_FROM_PTR(self); +} + +//| count: int +//| """How many triangles of the batch draw on the next refresh, clamped to the +//| buffer capacity. Assigning marks the layer dirty for a full repaint.""" +//| +//| +static mp_obj_t tri_get_count(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(((picogame_triangles_obj_t *)MP_OBJ_TO_PTR(self_in))->count); +} +static MP_DEFINE_CONST_FUN_OBJ_1(tri_get_count_obj, tri_get_count); +static mp_obj_t tri_set_count(mp_obj_t self_in, mp_obj_t v) { + picogame_triangles_obj_t *self = MP_OBJ_TO_PTR(self_in); + int n = mp_obj_get_int(v); + if (n < 0) { + n = 0; + } + if (n > self->cap) { + n = self->cap; + } + self->count = (uint16_t)n; + picogame_dirty_union(&self->dx1, 0, 0, 32767, 32767); // clipped to the play rect later + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(tri_set_count_obj, tri_set_count); +MP_PROPERTY_GETSET(tri_count_obj, (mp_obj_t)&tri_get_count_obj, (mp_obj_t)&tri_set_count_obj); + +static const mp_rom_map_elem_t picogame_triangles_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_count), MP_ROM_PTR(&tri_count_obj) }, +}; +static MP_DEFINE_CONST_DICT(picogame_triangles_locals_dict, picogame_triangles_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + picogame_triangles_type, + MP_QSTR_Triangles, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, picogame_triangles_make_new, + locals_dict, &picogame_triangles_locals_dict + ); diff --git a/shared-bindings/picogame/Triangles.h b/shared-bindings/picogame/Triangles.h new file mode 100644 index 00000000000..86175fcea51 --- /dev/null +++ b/shared-bindings/picogame/Triangles.h @@ -0,0 +1,11 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" + +extern const mp_obj_type_t picogame_triangles_type; diff --git a/shared-bindings/picogame/__init__.c b/shared-bindings/picogame/__init__.c new file mode 100644 index 00000000000..4e6e6f5e87e --- /dev/null +++ b/shared-bindings/picogame/__init__.c @@ -0,0 +1,1046 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT +// +// picogame: 2D game engine bindings for the PicoPad and similar boards. + +#include "py/runtime.h" +#include "shared-module/picogame/pg_compat.h" +#include "shared-bindings/busdisplay/BusDisplay.h" +#include "shared-bindings/picogame/__init__.h" +#include "shared-bindings/picogame/Bitmap.h" +#include "shared-bindings/picogame/Sprite.h" +#include "shared-bindings/picogame/Display.h" +#if CIRCUITPY_PICOGAME_FAST_DISPLAY +#include "common-hal/picogame/Display.h" // its struct (pg_get_display unwraps the wrapper) +#endif +#include "shared-bindings/picogame/Scene.h" +#include "shared-bindings/picogame/Tilemap.h" +#include "shared-bindings/picogame/Particles.h" +#include "shared-bindings/picogame/Canvas.h" +#include "shared-bindings/picogame/Framebuffer.h" +#include "shared-bindings/picogame/StripDraw.h" +#include "shared-bindings/picogame/Triangles.h" +#include "shared-module/picogame/__init__.h" +#include "shared-module/picogame/Bitmap.h" +#include "shared-module/picogame/Sprite.h" + +// --------------------------------------------------------------------------- +// Module-level functions +// --------------------------------------------------------------------------- + +// Cast a (possibly subclassed) BusDisplay arg to its native object, raising if it isn't one. +// Also accepts the pg.Display fast-DMA wrapper (unwrapped to its underlying busdisplay - the +// portable send path): any handle that identifies the panel works wherever a display is +// expected, so the same object a Scene renders through also works for render()/invert(). +// Without this, code holding the wrapper (custom setup, rgb444) worked on ports WITHOUT the +// fast backend and TypeError'd on ports WITH it. +#if CIRCUITPY_PICOGAME_FRAMEBUFFER +// Resolve a Framebuffer, or a subclass of it, to the native object; NULL otherwise. +static picogame_framebuffer_obj_t *pg_fb_native(mp_obj_t obj) { + mp_obj_t nfb = mp_obj_cast_to_native_base(obj, &picogame_framebuffer_type); + if (nfb != MP_OBJ_NULL && mp_obj_is_type(nfb, &picogame_framebuffer_type)) { + return MP_OBJ_TO_PTR(nfb); + } + return NULL; +} +#endif + +static busdisplay_busdisplay_obj_t *pg_get_display(mp_obj_t obj) { + #if CIRCUITPY_PICOGAME_FAST_DISPLAY + if (mp_obj_is_type(obj, &picogame_display_type)) { + return ((picogame_display_obj_t *)MP_OBJ_TO_PTR(obj))->display; + } + #endif + mp_obj_t native = mp_obj_cast_to_native_base(obj, &busdisplay_busdisplay_type); + if (!mp_obj_is_type(native, &busdisplay_busdisplay_type)) { + mp_arg_validate_type(native, &busdisplay_busdisplay_type, MP_QSTR_display); + } + return MP_OBJ_TO_PTR(native); +} + +//| """Rendering core for games +//| +//| The `picogame` module composites sprites, tilemaps, drawing canvases, +//| particle layers, 3D triangle batches and immediate-mode callbacks into a +//| small reusable strip buffer and sends each strip to the display, so a full-screen game does not +//| need a full-screen buffer. A :py:class:`Scene` tracks what changed and +//| repaints only those regions when :py:meth:`Scene.refresh` is called. +//| +//| A scene targets a :py:class:`~busdisplay.BusDisplay`, an accelerated +//| :py:class:`Display` or a RAM :py:class:`Framebuffer`. picogame drives the +//| display itself, so it requires ``display.auto_refresh = False`` and cannot +//| show displayio groups on the same display at the same time. +//| +//| Most programs start with :py:class:`Scene`, :py:class:`Sprite` and +//| :py:class:`Bitmap`. +//| +//| .. note:: +//| This module is the engine's rendering and compute core and is fully +//| usable on its own. The pure-Python `picogame helper libraries +//| `_ +//| (``picogame_game``, ``picogame_ray`` and others) build a complete game +//| framework on top of it: display, input and audio setup that adapts to +//| the board, game-loop timing, sprite pools and collision helpers, +//| animation, text, HUD and menus, sound effects and music, visual +//| effects, saved games, scene loading and pseudo-3D cameras. A desktop +//| simulator, asset-conversion tools, examples and tutorials live in the +//| `picogame repository `_, with +//| documentation at `picogame.makerclass.cz +//| `_. +//| +//| Example:: +//| +//| import array +//| import board +//| import time +//| +//| import picogame +//| +//| display = board.DISPLAY +//| display.auto_refresh = False +//| +//| size = display.width * picogame.STRIP_H * 2 +//| scene = picogame.Scene(display, bytearray(size), bytearray(size)) +//| +//| # One solid white 8x8 frame. +//| white = picogame.rgb565(255, 255, 255) +//| bitmap = picogame.Bitmap(array.array("H", [white] * 64), 8, 8) +//| player = scene.add(picogame.Sprite(bitmap, x=0, y=display.height // 2)) +//| +//| while True: +//| player.x = (player.x + 1) % display.width +//| scene.refresh() +//| time.sleep(0.02) +//| +//| This moves a white square across the screen, repainting only the pixels +//| that changed each frame.""" +//| +//| RGB565: int +//| """16-bit color bitmap format (transfer byte order).""" +//| PAL8: int +//| """8-bit paletted bitmap format.""" +//| +//| STRIP_H: int +//| """Recommended render-strip height in rows for this build. Sizing a `Scene` +//| strip buffer as ``display.width * STRIP_H * 2`` bytes yields strips this +//| tall.""" +//| +//| FPU: int +//| """``1`` when `project` uses hardware floating point (its buffers are +//| ``float32``), ``0`` when it uses signed 16.16 fixed-point ``int32``.""" +//| +//| API_LEVEL: int +//| """Version of the picogame API. Helper libraries compare this value against +//| the API level they target.""" +//| +//| RGB444_SUPPORTED: bool +//| """Whether `Display` supports ``rgb444=True`` on this board.""" +//| +//| FAST_DISPLAY_SUPPORTED: bool +//| """Whether the accelerated `Display` backend is available on this board.""" +//| +//| FRAMEBUFFER_SUPPORTED: bool +//| """Whether `Framebuffer` is available on this board.""" +//| +//| +//| def rgb565(r: int, g: int, b: int) -> int: +//| """Build an RGB565 color in the display's transfer byte order from 8-bit +//| components. Every color integer in this module is such a value.""" +//| ... +//| +//| +static mp_obj_t picogame_rgb565(mp_obj_t r_in, mp_obj_t g_in, mp_obj_t b_in) { + int r = mp_obj_get_int(r_in) & 0xff; + int g = mp_obj_get_int(g_in) & 0xff; + int b = mp_obj_get_int(b_in) & 0xff; + uint16_t c = ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | (b >> 3); + uint16_t wire = (uint16_t)((c >> 8) | (c << 8)); + return MP_OBJ_NEW_SMALL_INT(wire); +} +static MP_DEFINE_CONST_FUN_OBJ_3(picogame_rgb565_obj, picogame_rgb565); + +//| def invert( +//| display: Union[Display, busdisplay.BusDisplay, Framebuffer], on: bool +//| ) -> None: +//| """Enable or disable color inversion. +//| +//| Bus displays receive the controller's inversion command (INVON/INVOFF), which +//| takes effect immediately and sends no pixel data; the controller must support +//| it (ST7789 and ST7735 do). A `Framebuffer` target is instead inverted during +//| composition, starting with the next refresh.""" +//| ... +//| +//| +static mp_obj_t picogame_invert(mp_obj_t display_in, mp_obj_t on_in) { + #if CIRCUITPY_PICOGAME_FRAMEBUFFER + // A Framebuffer target (RP2350 DVI, the WASM playground) has no hardware INVON/INVOFF - + // emulate the flash by XORing the composite (mirrors the Scene/render Framebuffer handling). + { + mp_obj_t nfb = mp_obj_cast_to_native_base(display_in, &picogame_framebuffer_type); + if (nfb != MP_OBJ_NULL && mp_obj_is_type(nfb, &picogame_framebuffer_type)) { + picogame_fb_set_invert(mp_obj_is_true(on_in)); + return mp_const_none; + } + } + #endif + picogame_set_invert(pg_get_display(display_in), mp_obj_is_true(on_in)); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(picogame_invert_obj, picogame_invert); + +//| def render( +//| display: Union[Display, busdisplay.BusDisplay, Framebuffer], +//| layers: List[Union[Sprite, Tilemap, Canvas, Particles, StripDraw, Triangles]], +//| buffer: Optional[WriteableBuffer], +//| x0: int, +//| y0: int, +//| x1: int, +//| y1: int, +//| *, +//| background: int = 0, +//| ) -> None: +//| """Render ``layers`` into the screen region from ``(x0, y0)`` up to, but not +//| including, ``(x1, y1)``, and push it to ``display``, without a `Scene`. +//| +//| :param display: the render target +//| :param layers: layers of any kind, drawn bottom to top +//| :param ~circuitpython_typing.WriteableBuffer buffer: a reusable strip buffer of +//| at least ``(x1 - x0) * 2`` bytes; ignored (may be `None`) on a +//| `Framebuffer` target +//| :param int x0: left edge of the region +//| :param int y0: top edge of the region +//| :param int x1: right edge of the region (exclusive) +//| :param int y1: bottom edge of the region (exclusive) +//| :param int background: color the region is cleared to first""" +//| ... +//| +//| + +// Map a layer object to its PICOGAME_KIND_*, or raise the one shared TypeError. Both +// Scene.add() and pg.render() classify through here (one type chain, one message). +uint8_t picogame_kind_of(mp_obj_t o) { + if (mp_obj_is_type(o, &picogame_sprite_type)) { + return PICOGAME_KIND_SPRITE; + } + if (mp_obj_is_type(o, &picogame_stripdraw_type)) { + return PICOGAME_KIND_STRIPDRAW; + } + if (mp_obj_is_type(o, &picogame_tilemap_type)) { + return PICOGAME_KIND_TILEMAP; + } + if (mp_obj_is_type(o, &picogame_particles_type)) { + return PICOGAME_KIND_PARTICLES; + } + if (mp_obj_is_type(o, &picogame_canvas_type)) { + return PICOGAME_KIND_CANVAS; + } + if (mp_obj_is_type(o, &picogame_triangles_type)) { + return PICOGAME_KIND_TRIANGLES; + } + mp_arg_error_invalid(MP_QSTR_item); // the accepted layer types are listed in the docs +} + +static mp_obj_t picogame_render_fun(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_display, ARG_layers, ARG_buffer, ARG_x0, ARG_y0, ARG_x1, ARG_y1, ARG_background }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_display, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_layers, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_x0, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_y0, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_background, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Accept a picogame.Framebuffer (RAM scanout buffer) as the target too, when built + // in: immediate render composites straight into it (no strip buffer, no bus), so + // pg.render(board.DISPLAY, ...) works when board.DISPLAY is a Framebuffer - the HUD / + // HudBar / immediate-mode path on scanout-buffer platforms. Mirrors the Scene change. + #if CIRCUITPY_PICOGAME_FRAMEBUFFER + picogame_framebuffer_obj_t *fbt = + pg_fb_native(args[ARG_display].u_obj); + busdisplay_busdisplay_obj_t *display = fbt ? NULL : pg_get_display(args[ARG_display].u_obj); + #else + busdisplay_busdisplay_obj_t *display = pg_get_display(args[ARG_display].u_obj); + #endif + + size_t n = 0; + mp_obj_t *items; + mp_obj_get_array(args[ARG_layers].u_obj, &n, &items); + + // Classify items into layer kinds. All-Sprite lists stay on the NULL-kinds fast path (no alloc - + // the common case). Any non-Sprite layer (StripDraw/Canvas/Tilemap/Particles) builds a small kinds + // array so immediate render uses the SAME multi-layer blitter the Scene does - e.g. a StripDraw + // composited straight into the strip with `view.text()` = 0-RAM immediate HUD / text screen. + uint8_t kbuf[16]; + uint8_t *kinds = NULL; + for (size_t i = 0; i < n; i++) { + if (!mp_obj_is_type(items[i], &picogame_sprite_type)) { + kinds = (n <= MP_ARRAY_SIZE(kbuf)) ? kbuf : m_new(uint8_t, n); + break; + } + } + if (kinds != NULL) { + for (size_t i = 0; i < n; i++) { + // (an unknown type raises from kind_of; the GC reclaims a heap `kinds`) + kinds[i] = picogame_kind_of(items[i]); + } + } + + #if CIRCUITPY_PICOGAME_FRAMEBUFFER + if (fbt != NULL) { + // Framebuffer target: composite the region straight into it (no strip buffer, no + // bus). Same compositor as the SPI path; re-raise a latched StripDraw exception. + mp_obj_t exc = picogame_render_framebuffer(fbt->fb, fbt->width, fbt->height, fbt->fmt, + fbt->scratch, fbt->scratch_rows, + items, kinds, n, + args[ARG_x0].u_int, args[ARG_y0].u_int, args[ARG_x1].u_int, args[ARG_y1].u_int, + args[ARG_background].u_int, 0, 0); + if (kinds != NULL && n > MP_ARRAY_SIZE(kbuf)) { + m_del(uint8_t, kinds, n); + } + if (exc != MP_OBJ_NULL) { + nlr_raise(MP_OBJ_TO_PTR(exc)); + } + return mp_const_none; + } + #endif + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_WRITE); + + if (kinds == NULL) { + picogame_render(display, items, n, + (uint16_t *)bufinfo.buf, bufinfo.len / 2, + args[ARG_x0].u_int, args[ARG_y0].u_int, args[ARG_x1].u_int, args[ARG_y1].u_int, + args[ARG_background].u_int); + } else { + picogame_render_region(display, items, kinds, n, + (uint16_t *)bufinfo.buf, bufinfo.len / 2, + args[ARG_x0].u_int, args[ARG_y0].u_int, args[ARG_x1].u_int, args[ARG_y1].u_int, + args[ARG_background].u_int, 0, 0); + if (n > MP_ARRAY_SIZE(kbuf)) { + m_del(uint8_t, kinds, n); + } + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_KW(picogame_render_obj, 7, picogame_render_fun); + +//| def collide( +//| x1: int, y1: int, x2: int, y2: int, ax1: int, ay1: int, ax2: int = ..., ay2: int = ... +//| ) -> bool: +//| """Return whether an inclusive rectangle overlaps another rectangle or contains +//| a point. Both corners are part of the rectangle, so touching edges count as an +//| overlap. +//| +//| With six arguments, test rectangle ``(x1, y1, x2, y2)`` against the point +//| ``(ax1, ay1)``; with eight, test it against the rectangle +//| ``(ax1, ay1, ax2, ay2)``. +//| +//| Unlike the pixel regions used by `render`, whose upper bounds are excluded, +//| collision bounds are inclusive.""" +//| ... +//| +//| +static mp_obj_t picogame_collide(size_t n_args, const mp_obj_t *args) { + int x1 = mp_obj_get_int(args[0]); + int y1 = mp_obj_get_int(args[1]); + int x2 = mp_obj_get_int(args[2]); + int y2 = mp_obj_get_int(args[3]); + bool hit; + if (n_args == 8) { + int bx1 = mp_obj_get_int(args[4]); + int by1 = mp_obj_get_int(args[5]); + int bx2 = mp_obj_get_int(args[6]); + int by2 = mp_obj_get_int(args[7]); + hit = (x1 <= bx2) && (x2 >= bx1) && (y1 <= by2) && (y2 >= by1); + } else if (n_args == 6) { + int px = mp_obj_get_int(args[4]); + int py = mp_obj_get_int(args[5]); + hit = (px >= x1) && (px <= x2) && (py >= y1) && (py <= y2); + } else { + mp_raise_TypeError(MP_ERROR_TEXT("argument num/types mismatch")); + } + return mp_obj_new_bool(hit); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picogame_collide_obj, 6, 8, picogame_collide); + +// ---- procedural value-noise in C (the desktop simulator sim/picogame.py mirrors it) ---- +// The CANONICAL implementation is FIXED-POINT (Q16.16 coords, Q0.16 values), exposed +// under the plain names value2d/value1d/fbm2d/fbm1d (see further down). It benchmarked +// ~1.8x faster than float on-device (0.649 s vs 1.186 s / 5000 fbm2d), so the float +// version was retired (2026-06-18) to free flash for future engine features. +// The float reference is preserved but DISABLED in the `#if 0` below (cf. PicoLibSDK's +// own Noise2D, which is likewise float) - revive by flipping it to `#if 1` and pointing +// the module table at the *_obj names instead of the *_fx_obj ones. +#if 0 // float reference implementation - superseded by the fixed-point path below +static inline float pg_nhash(int32_t x, int32_t y, int32_t seed) { + uint32_t h = (uint32_t)x * 374761393u + (uint32_t)y * 668265263u + (uint32_t)seed * 362437u; + h = (h ^ (h >> 13)) * 1274126177u; + h = h ^ (h >> 16); + return (float)(h & 0xFFFFu) / 65535.0f; +} +static inline float pg_nsmooth(float t) { + return t * t * (3.0f - 2.0f * t); +} +static inline int32_t pg_ifloor(float x) { + int32_t i = (int32_t)x; + return (x < (float)i) ? i - 1 : i; +} +static float pg_value2d(float x, float y, int32_t seed) { + int32_t xi = pg_ifloor(x), yi = pg_ifloor(y); + float xf = x - (float)xi, yf = y - (float)yi; + float a = pg_nhash(xi, yi, seed), b = pg_nhash(xi + 1, yi, seed); + float c = pg_nhash(xi, yi + 1, seed), d = pg_nhash(xi + 1, yi + 1, seed); + float u = pg_nsmooth(xf), v = pg_nsmooth(yf); + return (a * (1.0f - u) + b * u) * (1.0f - v) + (c * (1.0f - u) + d * u) * v; +} +static float pg_value1d(float x, int32_t seed) { + int32_t xi = pg_ifloor(x); + float xf = x - (float)xi; + float a = pg_nhash(xi, 0, seed), b = pg_nhash(xi + 1, 0, seed); + return a + (b - a) * pg_nsmooth(xf); +} + +//| def value2d(x: float, y: float, *, seed: int = 0) -> float: +//| """Smooth 2-D value noise in ``0..1``.""" +//| ... +//| +//| +static mp_obj_t picogame_value2d(size_t n_args, const mp_obj_t *pos, mp_map_t *kw) { + static const mp_arg_t spec[] = { {MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, {MP_QSTR_seed, MP_ARG_INT, {.u_int = 0}} }; + mp_arg_val_t a[3]; + mp_arg_parse_all(n_args, pos, kw, 3, spec, a); + return mp_obj_new_float(pg_value2d(mp_obj_get_float(a[0].u_obj), mp_obj_get_float(a[1].u_obj), a[2].u_int)); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(picogame_value2d_obj, 2, picogame_value2d); + +//| def value1d(x: float, *, seed: int = 0) -> float: +//| """Smooth 1-D value noise in ``0..1``.""" +//| ... +static mp_obj_t picogame_value1d(size_t n_args, const mp_obj_t *pos, mp_map_t *kw) { + static const mp_arg_t spec[] = { {MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_seed, MP_ARG_INT, {.u_int = 0}} }; + mp_arg_val_t a[2]; + mp_arg_parse_all(n_args, pos, kw, 2, spec, a); + return mp_obj_new_float(pg_value1d(mp_obj_get_float(a[0].u_obj), a[1].u_int)); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(picogame_value1d_obj, 1, picogame_value1d); +#endif // float value2d / value1d + +// Shared arg spec for both fbm2d (disabled float) and fbm2d_fx (active fixed-point). +static const mp_arg_t pg_fbm2d_args[] = { + { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_octaves, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 4} }, + { MP_QSTR_seed, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_lacunarity, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_gain, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} }, +}; + +#if 0 // float reference fbm - superseded by the fixed-point path below +//| def fbm2d(x: float, y: float, *, octaves: int = 4, seed: int = 0, lacunarity: float = 2.0, gain: float = 0.5) -> float: +//| """Fractal (fBm) 2-D noise in ``0..1``: ``octaves`` layers of :py:func:`value2d`, each +//| ``lacunarity`` times finer and ``gain`` times weaker than the last.""" +//| ... +static mp_obj_t picogame_fbm2d(size_t n_args, const mp_obj_t *pos, mp_map_t *kw) { + mp_arg_val_t a[6]; + mp_arg_parse_all(n_args, pos, kw, 6, pg_fbm2d_args, a); + float x = mp_obj_get_float(a[0].u_obj), y = mp_obj_get_float(a[1].u_obj); + int octaves = a[2].u_int; + int32_t seed = a[3].u_int; + float lac = (a[4].u_obj == MP_OBJ_NULL) ? 2.0f : mp_obj_get_float(a[4].u_obj); + float gain = (a[5].u_obj == MP_OBJ_NULL) ? 0.5f : mp_obj_get_float(a[5].u_obj); + float total = 0.0f, amp = 1.0f, freq = 1.0f, norm = 0.0f; + for (int i = 0; i < octaves; i++) { + total += amp * pg_value2d(x * freq, y * freq, seed); + norm += amp; + amp *= gain; + freq *= lac; + } + return mp_obj_new_float(norm > 0.0f ? total / norm : 0.0f); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(picogame_fbm2d_obj, 2, picogame_fbm2d); + +//| def fbm1d(x: float, *, octaves: int = 4, seed: int = 0, lacunarity: float = 2.0, gain: float = 0.5) -> float: +//| """Fractal (fBm) 1-D noise in ``0..1``: ``octaves`` layers of +//| :py:func:`value1d`, each ``lacunarity`` times finer and ``gain`` times +//| weaker than the last.""" +//| ... +//| +//| +static mp_obj_t picogame_fbm1d(size_t n_args, const mp_obj_t *pos, mp_map_t *kw) { + static const mp_arg_t spec[] = { {MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_octaves, MP_ARG_INT, {.u_int = 4}}, {MP_QSTR_seed, MP_ARG_INT, {.u_int = 0}}, + {MP_QSTR_lacunarity, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, {MP_QSTR_gain, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}} }; + mp_arg_val_t a[5]; + mp_arg_parse_all(n_args, pos, kw, 5, spec, a); + float x = mp_obj_get_float(a[0].u_obj); + int octaves = a[1].u_int; + int32_t seed = a[2].u_int; + float lac = (a[3].u_obj == MP_OBJ_NULL) ? 2.0f : mp_obj_get_float(a[3].u_obj); + float gain = (a[4].u_obj == MP_OBJ_NULL) ? 0.5f : mp_obj_get_float(a[4].u_obj); + float total = 0.0f, amp = 1.0f, freq = 1.0f, norm = 0.0f; + for (int i = 0; i < octaves; i++) { + total += amp * pg_value1d(x * freq, seed); + norm += amp; + amp *= gain; + freq *= lac; + } + return mp_obj_new_float(norm > 0.0f ? total / norm : 0.0f); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(picogame_fbm1d_obj, 1, picogame_fbm1d); +#endif // float fbm2d / fbm1d + +// ---- fixed-point (Q16.16 coords, Q0.16 values) noise: the CANONICAL value-noise impl, +// exposed under the plain names value2d/value1d/fbm2d/fbm1d. The inner math is integer +// (float only at the Python boundary); ~1.8x faster than the retired float path. ---- +static inline uint32_t pg_nhash_raw(int32_t x, int32_t y, int32_t seed) { + uint32_t h = (uint32_t)x * 374761393u + (uint32_t)y * 668265263u + (uint32_t)seed * 362437u; + h = (h ^ (h >> 13)) * 1274126177u; + return (h ^ (h >> 16)) & 0xFFFFu; // Q0.16 in [0,1) +} +static inline uint32_t pg_smooth16(uint32_t t) { // t,result Q0.16: t*t*(3-2t) + uint32_t t2 = (t * t) >> 16; + uint32_t e = (3u << 16) - 2u * t; + return (uint32_t)(((uint64_t)t2 * e) >> 16); +} +static inline uint32_t pg_lerp16(uint32_t a, uint32_t b, uint32_t u) { + return (uint32_t)((int32_t)a + (int32_t)(((int64_t)((int32_t)b - (int32_t)a) * (int32_t)u) >> 16)); +} +static uint32_t pg_value2d_fx(int32_t X, int32_t Y, int32_t seed) { // X,Y Q16.16 -> Q0.16 + int32_t xi = X >> 16, yi = Y >> 16; + uint32_t xf = (uint32_t)(X - (xi << 16)), yf = (uint32_t)(Y - (yi << 16)); + uint32_t a = pg_nhash_raw(xi, yi, seed), b = pg_nhash_raw(xi + 1, yi, seed); + uint32_t c = pg_nhash_raw(xi, yi + 1, seed), d = pg_nhash_raw(xi + 1, yi + 1, seed); + uint32_t u = pg_smooth16(xf), v = pg_smooth16(yf); + return pg_lerp16(pg_lerp16(a, b, u), pg_lerp16(c, d, u), v); +} +// (1-D value noise == the 2-D sampler at Y=0, bit for bit: v = smooth16(0) = 0 makes the +// outer lerp return its first argument, which is exactly lerp(hash(xi,0), hash(xi+1,0), u). +// So the 1-D entry points below just call pg_value2d_fx(X, 0, seed) - no separate kernel.) +#define PG_Q16(f) ((int32_t)((f) * 65536.0f)) + +static mp_obj_t picogame_value2d_fx(size_t n_args, const mp_obj_t *pos, mp_map_t *kw) { + static const mp_arg_t spec[] = { {MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, {MP_QSTR_seed, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0}} }; + mp_arg_val_t a[3]; + mp_arg_parse_all(n_args, pos, kw, 3, spec, a); + int32_t v = pg_value2d_fx(PG_Q16(mp_obj_get_float(a[0].u_obj)), PG_Q16(mp_obj_get_float(a[1].u_obj)), a[2].u_int); + return mp_obj_new_float((float)v * (1.0f / 65536.0f)); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(picogame_value2d_fx_obj, 2, picogame_value2d_fx); + +static mp_obj_t picogame_value1d_fx(size_t n_args, const mp_obj_t *pos, mp_map_t *kw) { + static const mp_arg_t spec[] = { {MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_seed, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0}} }; + mp_arg_val_t a[2]; + mp_arg_parse_all(n_args, pos, kw, 2, spec, a); + int32_t v = pg_value2d_fx(PG_Q16(mp_obj_get_float(a[0].u_obj)), 0, a[1].u_int); + return mp_obj_new_float((float)v * (1.0f / 65536.0f)); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(picogame_value1d_fx_obj, 1, picogame_value1d_fx); + +// Shared fBm octave accumulator (the 1-D entry passes Y=0; sy is then 0 every octave, +// which the value sampler maps to the exact 1-D lattice - see the note above). +static mp_obj_t pg_fbm_eval(int32_t X, int32_t Y, int octaves, int32_t seed, + const mp_arg_val_t *lac, const mp_arg_val_t *gain) { + int32_t lacq = (lac->u_obj == MP_OBJ_NULL) ? (2 << 16) : PG_Q16(mp_obj_get_float(lac->u_obj)); + int32_t gainq = (gain->u_obj == MP_OBJ_NULL) ? (1 << 15) : PG_Q16(mp_obj_get_float(gain->u_obj)); + int32_t amp = 1 << 16, freq = 1 << 16; + int64_t total = 0, norm = 0; + for (int i = 0; i < octaves; i++) { + int32_t sx = (int32_t)(((int64_t)X * freq) >> 16), sy = (int32_t)(((int64_t)Y * freq) >> 16); + total += ((int64_t)amp * pg_value2d_fx(sx, sy, seed)) >> 16; + norm += amp; + amp = (int32_t)(((int64_t)amp * gainq) >> 16); + freq = (int32_t)(((int64_t)freq * lacq) >> 16); + } + return mp_obj_new_float(norm ? (float)total / (float)norm : 0.0f); +} + +static mp_obj_t picogame_fbm2d_fx(size_t n_args, const mp_obj_t *pos, mp_map_t *kw) { + mp_arg_val_t a[6]; + mp_arg_parse_all(n_args, pos, kw, 6, pg_fbm2d_args, a); + return pg_fbm_eval(PG_Q16(mp_obj_get_float(a[0].u_obj)), PG_Q16(mp_obj_get_float(a[1].u_obj)), + a[2].u_int, a[3].u_int, &a[4], &a[5]); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(picogame_fbm2d_fx_obj, 2, picogame_fbm2d_fx); + +static mp_obj_t picogame_fbm1d_fx(size_t n_args, const mp_obj_t *pos, mp_map_t *kw) { + static const mp_arg_t spec[] = { {MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_octaves, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 4}}, {MP_QSTR_seed, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0}}, + {MP_QSTR_lacunarity, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL}}, {MP_QSTR_gain, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL}} }; + mp_arg_val_t a[5]; + mp_arg_parse_all(n_args, pos, kw, 5, spec, a); + return pg_fbm_eval(PG_Q16(mp_obj_get_float(a[0].u_obj)), 0, + a[1].u_int, a[2].u_int, &a[3], &a[4]); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(picogame_fbm1d_fx_obj, 1, picogame_fbm1d_fx); + + +//| def project( +//| cam: ReadableBuffer, +//| pts: ReadableBuffer, +//| n: int, +//| out_sx: WriteableBuffer, +//| out_sy: WriteableBuffer, +//| ) -> None: +//| """Batch-project ``n`` 3D world points to screen coordinates. +//| +//| When :py:data:`FPU` is ``1``, ``cam`` and ``pts`` hold ``float32`` elements; +//| otherwise they hold signed 16.16 fixed-point ``int32`` elements. +//| +//| :param ~circuitpython_typing.ReadableBuffer cam: 15 camera parameters, in order: +//| eye x/y/z, right x/z (the camera cannot roll, so right has no y +//| component), up x/y/z, forward x/y/z, focal length, screen center x/y, +//| near-plane distance +//| :param ~circuitpython_typing.ReadableBuffer pts: ``3 * n`` world coordinates +//| (x, y, z per point) +//| :param int n: number of points +//| :param ~circuitpython_typing.WriteableBuffer out_sx: at least ``n`` ``int16`` +//| values; receives screen x, or the sentinel ``-32768`` for a point behind +//| the near plane +//| :param ~circuitpython_typing.WriteableBuffer out_sy: at least ``n`` ``int16`` +//| values; receives screen y, or ``-32768`` for a point behind the near plane""" +//| ... +//| +// project(cam, pts, n, out_sx, out_sy) - batch perspective projection of `n` 3D points to screen. +// cam = 15 camera params: ex,ey,ez, rx,rz, ux,uy,uz, fx,fy,fz, focal, cx0, cy0, near +// pts = n*3 world coords (x,y,z per point) +// out_sx/out_sy = int16 screen coords; a point behind the near plane gets sentinel -32768 +// On an FPU board (CIRCUITPY_PICOGAME_FPU) cam/pts are float32; else they are 16.16 fixed int32. +// This is the shared hot path for blocky pseudo-3D (project the 8 corners of each box, then fill). +static mp_obj_t picogame_project(size_t n_args, const mp_obj_t *args) { + mp_buffer_info_t ci, pi, xi, yi; + mp_get_buffer_raise(args[0], &ci, MP_BUFFER_READ); + mp_get_buffer_raise(args[1], &pi, MP_BUFFER_READ); + int n = mp_obj_get_int(args[2]); + mp_get_buffer_raise(args[3], &xi, MP_BUFFER_WRITE); + mp_get_buffer_raise(args[4], &yi, MP_BUFFER_WRITE); + int16_t *osx = xi.buf; + int16_t *osy = yi.buf; + if (n > (int)(xi.len >> 1)) { + n = (int)(xi.len >> 1); + } + #if CIRCUITPY_PICOGAME_FPU + const float *cam = ci.buf; + const float *pts = pi.buf; + float ex = cam[0], ey = cam[1], ez = cam[2]; + float rx = cam[3], rz = cam[4]; + float ux = cam[5], uy = cam[6], uz = cam[7]; + float fx = cam[8], fy = cam[9], fz = cam[10]; + float focal = cam[11], cx0 = cam[12], cy0 = cam[13], near = cam[14]; + for (int i = 0; i < n; i++) { + float X = pts[i * 3] - ex, Y = pts[i * 3 + 1] - ey, Z = pts[i * 3 + 2] - ez; + float cz = X * fx + Y * fy + Z * fz; + if (cz < near) { + osx[i] = -32768; + osy[i] = -32768; + continue; + } + float k = focal / cz; // hardware divide on an FPU part + osx[i] = (int16_t)(cx0 + (X * rx + Z * rz) * k); + osy[i] = (int16_t)(cy0 - (X * ux + Y * uy + Z * uz) * k); + } + #else + const int32_t *cam = ci.buf; // all values 16.16 + const int32_t *pts = pi.buf; + int32_t ex = cam[0], ey = cam[1], ez = cam[2]; + int32_t rx = cam[3], rz = cam[4]; + int32_t ux = cam[5], uy = cam[6], uz = cam[7]; + int32_t fx = cam[8], fy = cam[9], fz = cam[10]; + int32_t focal = cam[11], cx0 = cam[12], cy0 = cam[13], near = cam[14]; + // Full-precision Q16 dot products (int64 mul per term). A Q8-prescaled-basis/MULS variant was + // ~30% faster, but its error grows with |coord| (~0.2%/axis) and k = focal/cz AMPLIFIES it near + // the near plane - host-measured 23-34 px warps on close fly-bys at a file-browser world scale + // (walls visibly broke). Correctness first: Q16 keeps the worst error a few px at any cz >= near, + // for coords up to +-32k units; still ~4-5x faster than the same math in Python on the M0+. + #define FMUL(a, b) ((int32_t)(((int64_t)(a) * (b)) >> 16)) + for (int i = 0; i < n; i++) { + int32_t X = pts[i * 3] - ex, Y = pts[i * 3 + 1] - ey, Z = pts[i * 3 + 2] - ez; + int32_t cz = FMUL(X, fx) + FMUL(Y, fy) + FMUL(Z, fz); + if (cz < near) { + osx[i] = -32768; + osy[i] = -32768; + continue; + } + // focal/cz in 16.16. A 32-bit divide (focal<<8 = Q24, cz>>8 = Q8 -> Q16) is ~4x cheaper than + // an int64 divide on the M0+ (no HW divide) and the lost cz precision costs <0.02 px (host- + // measured). Needs FOCAL < ~250 (focal<<8 in uint32) and near >= 1/256 (cz>>8 nonzero). + int32_t k = (int32_t)(((uint32_t)focal << 8) / (uint32_t)(cz >> 8)); + int32_t rr = FMUL(X, rx) + FMUL(Z, rz); + int32_t uu = FMUL(X, ux) + FMUL(Y, uy) + FMUL(Z, uz); + osx[i] = (int16_t)((cx0 + FMUL(rr, k)) >> 16); + osy[i] = (int16_t)((cy0 - FMUL(uu, k)) >> 16); + } +#undef FMUL + #endif + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picogame_project_obj, 5, 5, picogame_project); + + + +//| def raycast( +//| map: ReadableBuffer, +//| mw: int, +//| mh: int, +//| posx: int, +//| posy: int, +//| lrx: int, +//| lry: int, +//| srx: int, +//| sry: int, +//| sh: int, +//| stride: int, +//| ncols: int, +//| wcolors: ReadableBuffer, +//| top: WriteableBuffer, +//| bot: WriteableBuffer, +//| col: WriteableBuffer, +//| dist: WriteableBuffer, +//| runs: Optional[WriteableBuffer] = None, +//| ) -> Optional[int]: +//| """Cast one frame of wall-finding rays across the screen columns of a +//| grid-map view. A low-level interface used by the ``picogame_ray`` helper, +//| which computes these inputs. +//| +//| All fixed-point arguments are signed 16.16 integers. +//| +//| :param ~circuitpython_typing.ReadableBuffer map: at least ``mw * mh`` wall-type +//| bytes, row-major; 0 is empty +//| :param int mw: map width in cells +//| :param int mh: map height in cells +//| :param int posx: camera x position (fixed-point) +//| :param int posy: camera y position (fixed-point) +//| :param int lrx: x of the column-0 ray direction (fixed-point) +//| :param int lry: y of the column-0 ray direction (fixed-point) +//| :param int srx: per-column ray step x (fixed-point) +//| :param int sry: per-column ray step y (fixed-point) +//| :param int sh: screen height in pixels +//| :param int stride: pixel width of one column +//| :param int ncols: number of columns to cast +//| :param ~circuitpython_typing.ReadableBuffer wcolors: two ``uint16`` colors per +//| wall type: near face at ``[type * 2]``, side face at ``[type * 2 + 1]`` +//| :param ~circuitpython_typing.WriteableBuffer top: at least ``ncols`` ``uint16`` +//| values; receives each column's wall top row +//| :param ~circuitpython_typing.WriteableBuffer bot: at least ``ncols`` ``uint16`` +//| values; receives each column's wall bottom row +//| :param ~circuitpython_typing.WriteableBuffer col: at least ``ncols`` ``uint16`` +//| values; receives each column's wall color +//| :param ~circuitpython_typing.WriteableBuffer dist: at least ``ncols`` ``int32`` +//| values; receives each column's perpendicular distance (fixed-point) +//| :param ~circuitpython_typing.WriteableBuffer runs: optional; at least +//| ``5 * ncols`` ``uint16`` values, laid out as five ``ncols``-long planes +//| (x0s, x1s, tops, bots, colors). When given, adjacent equal columns are +//| merged into wall runs suitable for :py:meth:`Canvas.vspans` and the run +//| count is returned; otherwise `None` is returned.""" +//| ... +//| +// C DDA wall raycaster for picogame_ray.Raycaster - INTEGER ONLY (16.16 fixed-point, no FPU; the paint, +// temporal invalidate, pose-cache and billboard math stay in Python; Python does the once-per-frame +// trig and passes Q16 ray params). map: read-only bytes, mw*mh wall types (0 = empty). pos*, l*x/l*y +// (leftRay, column 0), s*x/s*y (rayStep per column) are all 16.16. wcolors: uint16[(maxtype+1)*2] - +// [t*2] near, [t*2+1] side colour. top/bot/col: uint16 write buffers (len>=ncols); dist: int32 write +// buffer (perpendicular distance, 16.16). The int64 divides/muls are ONLY the per-column setup +// (O(ncols)); the DDA step loop is pure 32-bit. Mirrors the Python float fallback closely. +// Optional arg 17 (runs - ONE uint16 write buffer, len>=5*ncols, laid out as five ncols-long +// planes [x0s | x1s | tops | bots | colors]): also emit the RLE-MERGED wall runs (adjacent equal +// columns fused; x in PIXELS = column*stride) and return the run count. The planes feed +// Canvas.vspans directly as memoryview slices. This hoists picogame_ray's per-frame Python merge +// loop into the same C pass (measured 2-6.5 ms/frame of interpreted merge at stride=1 on RP2040). +// Callers clamp the LAST run's x1 to the screen width (stride rounding can overshoot by = 18) { // run outputs requested + mp_buffer_info_t q; + mp_get_buffer_raise(args[17], &q, MP_BUFFER_WRITE); + int cap = (int)(q.len / 10); // five uint16 planes + if (ncols > cap) { + ncols = cap; // never write past the run planes + } + r0 = q.buf; + r1 = r0 + cap; + rt = r1 + cap; + rb = rt + cap; + rcol = rb + cap; + } + const uint8_t *map = mi.buf; + const uint16_t *wc = wi.buf; + int wc_types = (int)(wi.len >> 2); + uint16_t *top = ti.buf; + uint16_t *bot = bi.buf; + uint16_t *col = ci.buf; + int32_t *dist_out = di.buf; // perpendicular distance, 16.16 + int half = sh >> 1; + int imapx0 = posx >> 16; + int imapy0 = posy >> 16; + int32_t fracx = posx & 0xFFFF; // fractional part of pos, 16.16 + int32_t fracy = posy & 0xFFFF; + const int32_t DD_CAP = (int32_t)1 << 24; // cap deltaDist so a 64-step accumulation stays in int32 + if (ncols > (int)(ti.len >> 1)) { + ncols = (int)(ti.len >> 1); + } + for (int c = 0; c < ncols; c++) { + int mapx = imapx0; + int mapy = imapy0; + int32_t ax = rdx < 0 ? -rdx : rdx; + int32_t ay = rdy < 0 ? -rdy : rdy; + // deltaDist = |1/rayDir| in 16.16 = (1<<32)/|rayDir_q16| (int64; per-column setup, not per-step) + int64_t ddx64 = ax ? (((int64_t)1 << 32) / ax) : (int64_t)DD_CAP; + int64_t ddy64 = ay ? (((int64_t)1 << 32) / ay) : (int64_t)DD_CAP; + int32_t ddx = ddx64 > DD_CAP ? DD_CAP : (int32_t)ddx64; + int32_t ddy = ddy64 > DD_CAP ? DD_CAP : (int32_t)ddy64; + int stepx, stepy; + int32_t sidex, sidey; + // sideDist to the first grid line = (fractional distance) * deltaDist, 16.16 (int64 mul, setup only) + if (rdx < 0) { + stepx = -1; + sidex = (int32_t)(((int64_t)fracx * ddx) >> 16); + } else { + stepx = 1; + sidex = (int32_t)(((int64_t)(65536 - fracx) * ddx) >> 16); + } + if (rdy < 0) { + stepy = -1; + sidey = (int32_t)(((int64_t)fracy * ddy) >> 16); + } else { + stepy = 1; + sidey = (int32_t)(((int64_t)(65536 - fracy) * ddy) >> 16); + } + int side = 0; + int cell = 1; + for (int i = 0; i < 64; i++) { // DDA - pure 32-bit + if (sidex < sidey) { + sidex += ddx; + mapx += stepx; + side = 0; + } else { + sidey += ddy; + mapy += stepy; + side = 1; + } + cell = (mapx >= 0 && mapx < mw && mapy >= 0 && mapy < mh) ? map[mapy * mw + mapx] : 1; + if (cell) { + break; + } + } + int32_t perp = (side == 0) ? (sidex - ddx) : (sidey - ddy); // perpWallDist, 16.16 + if (perp < 655) { + perp = 655; // ~0.01 in 16.16 + } + int lh = (int)(((int32_t)sh << 16) / perp); // sh / perpWallDist (px); 32-bit (sh<<16 <= ~15.7M) + int t = half - (lh >> 1); + int b = t + lh; + if (t < 0) { + t = 0; + } + if (b > sh) { + b = sh; + } + top[c] = (uint16_t)t; + bot[c] = (uint16_t)b; + int ct = (cell < wc_types) ? cell : 1; // unknown type -> type 1 (matches Python default) + col[c] = wc[ct * 2 + side]; + dist_out[c] = perp; + rdx += srx; // accumulate ray direction for the next column (no overflow) + rdy += sry; + } + if (r0 && ncols > 0) { + // post-pass RLE over the just-written (cache-hot) column arrays: one flush point + int nr = 0; + int rstart = 0; + for (int c = 1; c <= ncols; c++) { + if (c == ncols || top[c] != top[rstart] || bot[c] != bot[rstart] || col[c] != col[rstart]) { + r0[nr] = (uint16_t)(rstart * stride); + r1[nr] = (uint16_t)(c * stride); + rt[nr] = top[rstart]; + rb[nr] = bot[rstart]; + rcol[nr] = col[rstart]; + nr++; + rstart = c; + } + } + return MP_OBJ_NEW_SMALL_INT(nr); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picogame_raycast_obj, 17, 18, picogame_raycast); + + + +//| def road_edges( +//| rl: WriteableBuffer, +//| rr: WriteableBuffer, +//| hw: ReadableBuffer, +//| n: int, +//| cx0: int, +//| dist: int, +//| cfg: ReadableBuffer, +//| ) -> None: +//| """Compute one racing-road frame's left and right edge columns. A low-level +//| interface used by the ``picogame_road`` helper, which packs these inputs. +//| +//| Walks ``n`` screen rows bottom-up, accumulating the road curve, and writes the +//| edge x coordinates into ``rl`` and ``rr``. +//| +//| :param ~circuitpython_typing.WriteableBuffer rl: at least ``n`` ``int16`` values; +//| receives the left edge per row +//| :param ~circuitpython_typing.WriteableBuffer rr: at least ``n`` ``int16`` values; +//| receives the right edge per row +//| :param ~circuitpython_typing.ReadableBuffer hw: at least ``n`` ``int32`` per-row +//| half-widths (signed 16.16 fixed-point) +//| :param int n: number of rows to compute +//| :param int cx0: screen center x including lateral offset (signed 16.16 fixed-point) +//| :param int dist: integer world distance +//| :param ~circuitpython_typing.ReadableBuffer cfg: seven ``int32`` curve parameters, +//| in order: ``f1_q20``, ``f2_q20``, ``amp1k_q16``, ``amp2k_q16``, ``world_step``, +//| ``curve_step``, ``d_row_off``""" +//| ... +//| +// road_edges(rl, rr, hw, n, cx0, dist, cfg) - one racing-road frame's curve accumulator + integer +// edges in one call (the OutRun-genre compute_road loop; core + cfg layout documented in +// shared-module). rl/rr = int16 out, hw = int32 Q16 half-widths, cx0 = Q16 screen centre +// (incl. lateral), dist = integer world distance, cfg = int32[7]. +static mp_obj_t picogame_road_edges_fn(size_t n_args, const mp_obj_t *args) { + mp_buffer_info_t rli, rri, hwi, cfgi; + mp_get_buffer_raise(args[0], &rli, MP_BUFFER_WRITE); + mp_get_buffer_raise(args[1], &rri, MP_BUFFER_WRITE); + mp_get_buffer_raise(args[2], &hwi, MP_BUFFER_READ); + mp_get_buffer_raise(args[6], &cfgi, MP_BUFFER_READ); + int n = mp_obj_get_int(args[3]); + int cap = (int)(rli.len < rri.len ? rli.len : rri.len) / 2; + if (n > cap) { + n = cap; + } + if (n > (int)(hwi.len / 4)) { + n = (int)(hwi.len / 4); + } + if (n <= 0 || cfgi.len < 7 * 4) { + return mp_const_none; + } + picogame_road_edges((int16_t *)rli.buf, (int16_t *)rri.buf, (const int32_t *)hwi.buf, n, + mp_obj_get_int(args[4]), mp_obj_get_int(args[5]), (const int32_t *)cfgi.buf); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picogame_road_edges_obj, 7, 7, picogame_road_edges_fn); + + + +// Builds without a given display backend still expose its type: constructing it raises, the same +// way an unsupported rgb444= does, instead of the module changing shape with build flags. +#if !CIRCUITPY_PICOGAME_FAST_DISPLAY || !CIRCUITPY_PICOGAME_FRAMEBUFFER +static mp_obj_t pg_stub_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("Operation or feature not supported")); +} +#endif +#if !CIRCUITPY_PICOGAME_FAST_DISPLAY +MP_DEFINE_CONST_OBJ_TYPE(picogame_display_type, MP_QSTR_Display, MP_TYPE_FLAG_NONE, make_new, pg_stub_make_new); +#endif +#if !CIRCUITPY_PICOGAME_FRAMEBUFFER +MP_DEFINE_CONST_OBJ_TYPE(picogame_framebuffer_type, MP_QSTR_Framebuffer, MP_TYPE_FLAG_NONE, make_new, pg_stub_make_new); +#endif + +static const mp_rom_map_elem_t picogame_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_picogame) }, + { MP_ROM_QSTR(MP_QSTR_Bitmap), MP_ROM_PTR(&picogame_bitmap_type) }, + { MP_ROM_QSTR(MP_QSTR_Sprite), MP_ROM_PTR(&picogame_sprite_type) }, + { MP_ROM_QSTR(MP_QSTR_Display), MP_ROM_PTR(&picogame_display_type) }, + { MP_ROM_QSTR(MP_QSTR_Scene), MP_ROM_PTR(&picogame_scene_type) }, + { MP_ROM_QSTR(MP_QSTR_Tilemap), MP_ROM_PTR(&picogame_tilemap_type) }, + { MP_ROM_QSTR(MP_QSTR_Particles), MP_ROM_PTR(&picogame_particles_type) }, + { MP_ROM_QSTR(MP_QSTR_Canvas), MP_ROM_PTR(&picogame_canvas_type) }, + { MP_ROM_QSTR(MP_QSTR_StripDraw), MP_ROM_PTR(&picogame_stripdraw_type) }, + { MP_ROM_QSTR(MP_QSTR_Triangles), MP_ROM_PTR(&picogame_triangles_type) }, + { MP_ROM_QSTR(MP_QSTR_Framebuffer), MP_ROM_PTR(&picogame_framebuffer_type) }, + { MP_ROM_QSTR(MP_QSTR_render), MP_ROM_PTR(&picogame_render_obj) }, + { MP_ROM_QSTR(MP_QSTR_raycast), MP_ROM_PTR(&picogame_raycast_obj) }, + { MP_ROM_QSTR(MP_QSTR_road_edges), MP_ROM_PTR(&picogame_road_edges_obj) }, + { MP_ROM_QSTR(MP_QSTR_project), MP_ROM_PTR(&picogame_project_obj) }, + // True when the pseudo-3D/math primitives use the hardware-float path (FPU board). Python packs + // camera/point buffers as float32 when this is set, else as 16.16 fixed int32. + { MP_ROM_QSTR(MP_QSTR_FPU), MP_ROM_INT(CIRCUITPY_PICOGAME_FPU) }, + { MP_ROM_QSTR(MP_QSTR_invert), MP_ROM_PTR(&picogame_invert_obj) }, + { MP_ROM_QSTR(MP_QSTR_collide), MP_ROM_PTR(&picogame_collide_obj) }, + // Canonical noise = the fixed-point implementation (float retired; see `#if 0` above). + { MP_ROM_QSTR(MP_QSTR_value2d), MP_ROM_PTR(&picogame_value2d_fx_obj) }, + { MP_ROM_QSTR(MP_QSTR_value1d), MP_ROM_PTR(&picogame_value1d_fx_obj) }, + { MP_ROM_QSTR(MP_QSTR_fbm2d), MP_ROM_PTR(&picogame_fbm2d_fx_obj) }, + { MP_ROM_QSTR(MP_QSTR_fbm1d), MP_ROM_PTR(&picogame_fbm1d_fx_obj) }, + { MP_ROM_QSTR(MP_QSTR_rgb565), MP_ROM_PTR(&picogame_rgb565_obj) }, + { MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(PICOGAME_FMT_RGB565) }, + { MP_ROM_QSTR(MP_QSTR_PAL8), MP_ROM_INT(PICOGAME_FMT_PAL8) }, + // Engine API level: bump by 1 whenever the PYTHON-VISIBLE surface grows (new method/property/ + // module function/constant), so picogame-libs can diagnose a too-old firmware up front + // ("needs API_LEVEL >= N") instead of failing later with a random missing attribute. + // Level 1 = the 2026-07 surface (post API-freeze + Canvas.text/Framebuffer/StripDraw + // always_dirty). Older firmwares have no attribute at all -> getattr(pg, "API_LEVEL", 0). + { MP_ROM_QSTR(MP_QSTR_API_LEVEL), MP_ROM_INT(1) }, + // Build-time capability flag: does THIS board's panel controller support 12-bit RGB444 + // (COLMOD)? The board declares it (it knows its controller); a game reads it to enable + // Display(rgb444=...) only where it works - one codebase runs on ST7789 AND ILI9341. + #if CIRCUITPY_PICOGAME_RGB444 + { MP_ROM_QSTR(MP_QSTR_RGB444_SUPPORTED), MP_ROM_TRUE }, + #else + { MP_ROM_QSTR(MP_QSTR_RGB444_SUPPORTED), MP_ROM_FALSE }, + #endif + #if CIRCUITPY_PICOGAME_FAST_DISPLAY + { MP_ROM_QSTR(MP_QSTR_FAST_DISPLAY_SUPPORTED), MP_ROM_TRUE }, + #else + { MP_ROM_QSTR(MP_QSTR_FAST_DISPLAY_SUPPORTED), MP_ROM_FALSE }, + #endif + #if CIRCUITPY_PICOGAME_FRAMEBUFFER + { MP_ROM_QSTR(MP_QSTR_FRAMEBUFFER_SUPPORTED), MP_ROM_TRUE }, + #else + { MP_ROM_QSTR(MP_QSTR_FRAMEBUFFER_SUPPORTED), MP_ROM_FALSE }, + #endif + // Build-time default render-strip height (rows). picogame_game.setup() uses it when strip_h is + // None; games can override per call; a board can override the default in mpconfigboard.h. + // MEASURED (RP2040): with async DMA double-buffering, SMALL strips overlap render+transfer best -> + // 8 is both fastest and least RAM (the two w*strip_h*2 buffers shrink). WITHOUT the DMA backend + // there's no overlap, so a blocking send per strip makes LARGER strips win -> 24. + #ifndef PICOGAME_STRIP_H + #if CIRCUITPY_PICOGAME_FAST_DISPLAY + #define PICOGAME_STRIP_H 8 + #else + #define PICOGAME_STRIP_H 24 + #endif + #endif + { MP_ROM_QSTR(MP_QSTR_STRIP_H), MP_ROM_INT(PICOGAME_STRIP_H) }, +}; +static MP_DEFINE_CONST_DICT(picogame_module_globals, picogame_module_globals_table); + +const mp_obj_module_t picogame_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&picogame_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_picogame, picogame_module); diff --git a/shared-bindings/picogame/__init__.h b/shared-bindings/picogame/__init__.h new file mode 100644 index 00000000000..9bfca623beb --- /dev/null +++ b/shared-bindings/picogame/__init__.h @@ -0,0 +1,12 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" + + +uint8_t picogame_kind_of(mp_obj_t o); diff --git a/shared-bindings/socketpool/SocketPool.c b/shared-bindings/socketpool/SocketPool.c index 1fc9e2c3335..f50a7a5fcfe 100644 --- a/shared-bindings/socketpool/SocketPool.c +++ b/shared-bindings/socketpool/SocketPool.c @@ -190,15 +190,27 @@ MP_DEFINE_CONST_OBJ_TYPE( ); MP_WEAK MP_NORETURN -void common_hal_socketpool_socketpool_raise_gaierror_noname(void) { +void common_hal_socketpool_socketpool_raise_gaierror(int err) { vstr_t vstr; mp_print_t print; vstr_init_print(&vstr, 64, &print); - mp_printf(&print, "%S", MP_ERROR_TEXT("Name or service not known")); + // Only EAI_NONAME means the name was not found. Other codes cover + // unrelated failures, such as an unsupported family, so use the generic + // message for those rather than one that would be misleading. + if (err == SOCKETPOOL_EAI_NONAME) { + mp_printf(&print, "%S", MP_ERROR_TEXT("Name or service not known")); + } else { + mp_cprintf(&print, MP_ERROR_TEXT("%q failure: %d"), MP_QSTR_getaddrinfo, err); + } mp_obj_t exc_args[] = { - MP_OBJ_NEW_SMALL_INT(SOCKETPOOL_EAI_NONAME), + MP_OBJ_NEW_SMALL_INT(err), mp_obj_new_str_from_vstr(&vstr), }; nlr_raise(mp_obj_new_exception_args(&mp_type_gaierror, MP_ARRAY_SIZE(exc_args), exc_args)); } + +MP_WEAK MP_NORETURN +void common_hal_socketpool_socketpool_raise_gaierror_noname(void) { + common_hal_socketpool_socketpool_raise_gaierror(SOCKETPOOL_EAI_NONAME); +} diff --git a/shared-bindings/socketpool/SocketPool.h b/shared-bindings/socketpool/SocketPool.h index 60d58372b91..1d0a3bbccff 100644 --- a/shared-bindings/socketpool/SocketPool.h +++ b/shared-bindings/socketpool/SocketPool.h @@ -26,5 +26,6 @@ bool socketpool_socket(socketpool_socketpool_obj_t *self, int proto, socketpool_socket_obj_t *sock); MP_NORETURN void common_hal_socketpool_socketpool_raise_gaierror_noname(void); +MP_NORETURN void common_hal_socketpool_socketpool_raise_gaierror(int err); mp_obj_t common_hal_socketpool_getaddrinfo_raise(socketpool_socketpool_obj_t *self, const char *host, int port, int family, int type, int proto, int flags); diff --git a/shared-bindings/traceback/__init__.c b/shared-bindings/traceback/__init__.c index ca1e6fe976a..651ec2c0142 100644 --- a/shared-bindings/traceback/__init__.c +++ b/shared-bindings/traceback/__init__.c @@ -36,10 +36,18 @@ static void traceback_exception_common(bool is_print_exception, mp_print_t *prin mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); mp_obj_t value = args[ARG_value].u_obj; + mp_obj_t tb_obj = args[ARG_tb].u_obj; + + // Both or neither of value and tb must be supplied. + if ((value != MP_OBJ_NULL && tb_obj == MP_OBJ_NULL) || + (value == MP_OBJ_NULL && tb_obj != MP_OBJ_NULL)) { + mp_raise_ValueError_varg(MP_ERROR_TEXT("Supply both or neither of %q and %q"), MP_QSTR_value, MP_QSTR_tb); + } + if (value == MP_OBJ_NULL) { value = args[ARG_exc].u_obj; } - mp_obj_t tb_obj = args[ARG_tb].u_obj; + mp_obj_t limit_obj = args[ARG_limit].u_obj; #if MICROPY_CPYTHON_EXCEPTION_CHAIN bool chain = args[ARG_chain].u_bool; @@ -105,21 +113,21 @@ static void traceback_exception_common(bool is_print_exception, mp_print_t *prin //| def format_exception( //| exc: BaseException | Type[BaseException], //| /, -//| value: Optional[BaseException] = None, +//| value: BaseException, //| tb: Optional[TracebackType] = None, //| limit: Optional[int] = None, //| chain: Optional[bool] = True, //| ) -> List[str]: //| """Format a stack trace and the exception information. //| -//| If the exception value is passed in ``exc``, then this exception value and its -//| associated traceback are used. This is compatible with CPython 3.10 and newer. +//| If the exception value is passed in ``exc``, and ``value`` and ``tb`` are not supplied, +//| then this exception value and its associated traceback are used. +//| This is compatible with CPython 3.10 and newer. //| -//| If the exception value is passed in ``value``, then any value passed in for -//| ``exc`` is ignored. ``value`` is used as the exception value and the -//| traceback in the ``tb`` argument is used. In this case, if ``tb`` is None, -//| no traceback will be shown. This is compatible with CPython 3.5 and -//| newer. +//| If the exception value is passed in ``value``, then both ``value`` and ``tb`` must be supplied. +//| Any value passed in for ``exc`` is *ignored*. ``value`` is used as the exception value and the +//| traceback in the ``tb`` argument is used. In this case, if ``tb`` is `None`, +//| no traceback will be shown. This is compatible with CPython 3.5 and newer. //| //| The arguments have the same meaning as the corresponding arguments //| to print_exception(). The return value is a list of strings, each @@ -128,8 +136,8 @@ static void traceback_exception_common(bool is_print_exception, mp_print_t *prin //| printed as does print_exception(). //| //| :param exc: The exception. Must be an instance of `BaseException`. Unused if value is specified. -//| :param value: If specified, is used in place of ``exc``. -//| :param TracebackType tb: When value is alsp specified, ``tb`` is used in place of the exception's own traceback. If `None`, the traceback will not be printed. +//| :param value: If specified, is used in place of ``exc``, and ``tb`` must also be specified. +//| :param tb: When ``value`` is also specified, ``tb`` is used in place of the exception's own traceback. If `None`, the traceback will not be printed. //| :param int limit: Print up to limit stack trace entries (starting from the caller’s frame) if limit is positive. //| Otherwise, print the last ``abs(limit)`` entries. If limit is omitted or None, all entries are printed. //| :param bool chain: If `True` then chained exceptions will be printed. @@ -150,7 +158,7 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(traceback_format_exception_obj, 0, traceback_f //| def print_exception( //| exc: BaseException | Type[BaseException], //| /, -//| value: Optional[BaseException] = None, +//| value: BaseException, //| tb: Optional[TracebackType] = None, //| limit: Optional[int] = None, //| file: Optional[io.FileIO] = None, @@ -158,18 +166,18 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(traceback_format_exception_obj, 0, traceback_f //| ) -> None: //| """Prints exception information and stack trace entries. //| -//| If the exception value is passed in ``exc``, then this exception value and its -//| associated traceback are used. This is compatible with CPython 3.10 and newer. +//| If the exception value is passed in ``exc``, and ``value`` and ``tb`` are not supplied, +//| then this exception value and its associated traceback are used. +//| This is compatible with CPython 3.10 and newer. //| -//| If the exception value is passed in ``value``, then any value passed in for -//| ``exc`` is ignored. ``value`` is used as the exception value and the -//| traceback in the ``tb`` argument is used. In this case, if ``tb`` is None, -//| no traceback will be shown. This is compatible with CPython 3.5 and -//| newer. +//| If the exception value is passed in ``value``, then both ``value`` and ``tb`` must be supplied. +//| Any value passed in for ``exc`` is *ignored*. ``value`` is used as the exception value and the +//| traceback in the ``tb`` argument is used. In this case, if ``tb`` is `None`, +//| no traceback will be shown. This is compatible with CPython 3.5 and newer. //| //| :param exc: The exception. Must be an instance of `BaseException`. Unused if value is specified. -//| :param value: If specified, is used in place of ``exc``. -//| :param tb: When value is alsp specified, ``tb`` is used in place of the exception's own traceback. If `None`, the traceback will not be printed. +//| :param value: If specified, is used in place of ``exc``, and ``tb`` must also be specified. +//| :param tb: When ``value`` is also specified, ``tb`` is used in place of the exception's own traceback. If `None`, the traceback will not be printed. //| :param int limit: Print up to limit stack trace entries (starting from the caller’s frame) if limit is positive. //| Otherwise, print the last ``abs(limit)`` entries. If limit is omitted or None, all entries are printed. //| :param io.FileIO file: If file is omitted or `None`, the output goes to `sys.stderr`; otherwise it should be an open diff --git a/shared-bindings/usb_audio/USBMicrophone.c b/shared-bindings/usb_audio/USBMicrophone.c index ff53d812e49..c66c78942dd 100644 --- a/shared-bindings/usb_audio/USBMicrophone.c +++ b/shared-bindings/usb_audio/USBMicrophone.c @@ -12,6 +12,7 @@ #include "shared-bindings/usb_audio/USBMicrophone.h" #include "shared-bindings/util.h" #include "shared-module/usb_audio/__init__.h" +#include "shared-module/usb_audio/usb_audio_descriptors.h" //| class USBMicrophone: //| """Streams an audio sample to the host computer as a USB Audio Class microphone. diff --git a/shared-bindings/usb_audio/USBSpeaker.c b/shared-bindings/usb_audio/USBSpeaker.c index d9302886c64..78c5e6099b9 100644 --- a/shared-bindings/usb_audio/USBSpeaker.c +++ b/shared-bindings/usb_audio/USBSpeaker.c @@ -33,7 +33,7 @@ //| //| # boot.py //| import usb_audio -//| usb_audio.enable(sample_rate=16000, channel_count=1, bits_per_sample=16, +//| usb_audio.enable(sample_rate=16000, channel_count=1, //| microphone=False, speaker=True) //| //| .. code-block:: py diff --git a/shared-bindings/usb_audio/__init__.c b/shared-bindings/usb_audio/__init__.c index 49eaa9716f2..31d3ab549fd 100644 --- a/shared-bindings/usb_audio/__init__.c +++ b/shared-bindings/usb_audio/__init__.c @@ -19,9 +19,9 @@ //| Audio Class (UAC2) microphone: the board is the audio *source* and streams //| samples to the host over a USB isochronous IN endpoint. //| -//| This mode requires 1 IN endpoint and 2 interfaces. Generally, microcontrollers -//| have a limit on the number of endpoints. If you exceed the number of endpoints, -//| CircuitPython will automatically enter Safe Mode. Even in this case, you may be +//| This mode requires 2 IN endpoints and 2 interfaces. +//| Generally, microcontrollers have a limit on the number of endpoints. If you exceed the number +//| of endpoints, CircuitPython will automatically enter Safe Mode. Even in this case, you may be //| able to enable USB audio by also disabling other USB functions, such as //| `usb_hid` or `usb_midi`. //| @@ -32,7 +32,7 @@ //| //| # boot.py //| import usb_audio -//| usb_audio.enable(sample_rate=16000, channel_count=1, bits_per_sample=16) +//| usb_audio.enable(sample_rate=16000) //| //| .. code-block:: py //| @@ -61,9 +61,8 @@ //| pass //| mic.stop() //| -//| The ``sample_rate`` and ``channel_count`` of the sample played must match the -//| values passed to `enable`, and the sample must be 16-bit signed; otherwise -//| ``play`` raises a ``ValueError``. +//| The ``sample_rate`` and ``channel_count`` of the sample played must match the values passed to `enable`, +//| and the sample must be 16-bit signed; otherwise ``play`` raises a ``ValueError``. //| //| This interface is experimental and may change without notice even in stable //| versions of CircuitPython.""" @@ -84,7 +83,6 @@ //| def enable( //| sample_rate: int = 16000, //| channel_count: int = 1, -//| bits_per_sample: int = 16, //| microphone: bool = True, //| speaker: bool = False, //| ) -> None: @@ -93,8 +91,7 @@ //| This function may only be used from ``boot.py``. //| //| :param int sample_rate: Samples per second of the streamed audio. -//| :param int channel_count: Number of channels. Only mono (1) is supported initially. -//| :param int bits_per_sample: Bits per signed PCM sample. Only 16 is supported initially. +//| :param int channel_count: Number of channels. Either mono (1) or stereo (2) is supported. //| :param bool microphone: Present a microphone (audio flows board -> host). Enabled by default. //| :param bool speaker: Present a speaker (audio flows host -> board). //| @@ -103,11 +100,10 @@ //| //| static mp_obj_t usb_audio_enable(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_sample_rate, ARG_channel_count, ARG_bits_per_sample, ARG_microphone, ARG_speaker }; + enum { ARG_sample_rate, ARG_channel_count, ARG_microphone, ARG_speaker }; static const mp_arg_t allowed_args[] = { { MP_QSTR_sample_rate, MP_ARG_INT, { .u_int = 16000 } }, { MP_QSTR_channel_count, MP_ARG_INT, { .u_int = 1 } }, - { MP_QSTR_bits_per_sample, MP_ARG_INT, { .u_int = 16 } }, { MP_QSTR_microphone, MP_ARG_BOOL, { .u_bool = true } }, { MP_QSTR_speaker, MP_ARG_BOOL, { .u_bool = false } }, }; @@ -116,7 +112,6 @@ static mp_obj_t usb_audio_enable(size_t n_args, const mp_obj_t *pos_args, mp_map mp_int_t sample_rate = mp_arg_validate_int_range(args[ARG_sample_rate].u_int, 1, USB_AUDIO_MAX_SAMPLE_RATE, MP_QSTR_sample_rate); mp_int_t channel_count = mp_arg_validate_int_range(args[ARG_channel_count].u_int, 1, USB_AUDIO_N_CHANNELS, MP_QSTR_channel_count); - mp_int_t bits_per_sample = mp_arg_validate_int(args[ARG_bits_per_sample].u_int, USB_AUDIO_BITS_PER_SAMPLE, MP_QSTR_bits_per_sample); bool microphone = args[ARG_microphone].u_bool; bool speaker = args[ARG_speaker].u_bool; @@ -124,7 +119,7 @@ static mp_obj_t usb_audio_enable(size_t n_args, const mp_obj_t *pos_args, mp_map mp_raise_ValueError(MP_ERROR_TEXT("At least one of microphone and speaker must be enabled")); } - if (!shared_module_usb_audio_enable(sample_rate, channel_count, bits_per_sample, microphone, speaker)) { + if (!shared_module_usb_audio_enable(sample_rate, channel_count, microphone, speaker)) { mp_raise_RuntimeError(MP_ERROR_TEXT("Cannot change USB devices now")); } diff --git a/shared-module/_bleio/Address.c b/shared-module/_bleio/Address.c index b40abb3d560..5a50ba4f28f 100644 --- a/shared-module/_bleio/Address.c +++ b/shared-module/_bleio/Address.c @@ -7,17 +7,24 @@ #include -#include "py/objstr.h" +#include "py/obj.h" #include "shared-bindings/_bleio/Address.h" #include "shared-module/_bleio/Address.h" -void common_hal_bleio_address_construct(bleio_address_obj_t *self, uint8_t *bytes, uint8_t address_type) { - self->bytes = mp_obj_new_bytes(bytes, NUM_BLEIO_ADDRESS_BYTES); +void common_hal_bleio_address_construct(bleio_address_obj_t *self, const uint8_t *bytes, uint8_t address_type) { + memcpy(self->bytes, bytes, NUM_BLEIO_ADDRESS_BYTES); self->type = address_type; } mp_obj_t common_hal_bleio_address_get_address_bytes(bleio_address_obj_t *self) { - return self->bytes; + // Build a bytes object on demand. Address stores its bytes inline, so this + // is only reached from Python (e.g. ``address.address_bytes``) where the + // heap is available. + return mp_obj_new_bytes(self->bytes, NUM_BLEIO_ADDRESS_BYTES); +} + +void common_hal_bleio_address_get_bytes(bleio_address_obj_t *self, uint8_t bytes[NUM_BLEIO_ADDRESS_BYTES]) { + memcpy(bytes, self->bytes, NUM_BLEIO_ADDRESS_BYTES); } uint8_t common_hal_bleio_address_get_type(bleio_address_obj_t *self) { diff --git a/shared-module/_bleio/Address.h b/shared-module/_bleio/Address.h index 76e7a1177fc..55324770375 100644 --- a/shared-module/_bleio/Address.h +++ b/shared-module/_bleio/Address.h @@ -14,5 +14,8 @@ typedef struct { mp_obj_base_t base; uint8_t type; - mp_obj_t bytes; // a bytes() object + // Little-endian BD_ADDR order: bytes[0] is the least-significant byte. + // Stored inline so an Address can live in static storage (e.g. embedded in + // an adapter) without allocating a separate bytes object. + uint8_t bytes[NUM_BLEIO_ADDRESS_BYTES]; } bleio_address_obj_t; diff --git a/shared-module/audiodelays/Echo.c b/shared-module/audiodelays/Echo.c index b2501b7d01e..a57ed237c48 100644 --- a/shared-module/audiodelays/Echo.c +++ b/shared-module/audiodelays/Echo.c @@ -11,7 +11,7 @@ #include void common_hal_audiodelays_echo_construct(audiodelays_echo_obj_t *self, uint32_t max_delay_ms, - mp_obj_t delay_ms, mp_obj_t decay, mp_obj_t mix, + mp_obj_t delay_ms, mp_obj_t decay, mp_obj_t filter, mp_obj_t mix, uint32_t buffer_size, uint8_t bits_per_sample, bool samples_signed, uint8_t channel_count, uint32_t sample_rate, bool freq_shift) { @@ -59,6 +59,8 @@ void common_hal_audiodelays_echo_construct(audiodelays_echo_obj_t *self, uint32_ // The below section sets up the echo effect's starting values. For a different effect this section will change + common_hal_audiodelays_echo_set_filter(self, filter); + // If we did not receive a BlockInput we need to create a default float value if (decay == MP_OBJ_NULL) { decay = mp_obj_new_float(MICROPY_FLOAT_CONST(0.7)); @@ -106,6 +108,7 @@ void common_hal_audiodelays_echo_construct(audiodelays_echo_obj_t *self, uint32_ void common_hal_audiodelays_echo_deinit(audiodelays_echo_obj_t *self) { audiosample_mark_deinit(&self->base); + audiofilters_deinit_filter_chain(&self->filter); self->echo_buffer = NULL; self->buffer[0] = NULL; self->buffer[1] = NULL; @@ -166,6 +169,14 @@ void common_hal_audiodelays_echo_set_decay(audiodelays_echo_obj_t *self, mp_obj_ synthio_block_assign_slot(decay, &self->decay, MP_QSTR_decay); } +mp_obj_t common_hal_audiodelays_echo_get_filter(audiodelays_echo_obj_t *self) { + return self->filter.obj; +} + +void common_hal_audiodelays_echo_set_filter(audiodelays_echo_obj_t *self, mp_obj_t filter_in) { + audiofilters_assign_filter_chain(&self->filter, filter_in, self->base.channel_count); +} + mp_obj_t common_hal_audiodelays_echo_get_mix(audiodelays_echo_obj_t *self) { return self->mix.obj; } @@ -197,6 +208,8 @@ void audiodelays_echo_reset_buffer(audiodelays_echo_obj_t *self, memset(self->buffer[0], 0, self->buffer_len); memset(self->buffer[1], 0, self->buffer_len); memset(self->echo_buffer, 0, self->max_echo_buffer_len); + + audiofilters_reset_filter_chain(&self->filter, self->base.channel_count); } bool common_hal_audiodelays_echo_get_playing(audiodelays_echo_obj_t *self) { @@ -289,6 +302,9 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t * recalculate_delay(self, f_delay_ms); } + // Tick biquad filters + audiofilters_tick_filter_chain(&self->filter); + uint32_t echo_buf_len = self->echo_buffer_len / sizeof(uint16_t); uint32_t max_echo_buf_len = (self->max_echo_buffer_len >> (self->base.channel_count - 1)) / sizeof(uint16_t); @@ -324,12 +340,12 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t * for (uint32_t j = echo_buffer_pos >> 8; j < next_buffer_pos >> 8; j++) { word = (int16_t)(echo_buffer[(j % echo_buf_len) + echo_buffer_offset] * decay); - echo_buffer[(j % echo_buf_len) + echo_buffer_offset] = word; + echo_buffer[(j % echo_buf_len) + echo_buffer_offset] = (int16_t)audiofilters_process_filter_chain(&self->filter, self->base.channel_count, !!echo_buffer_offset, word); } } else { echo = echo_buffer[echo_buffer_pos + echo_buffer_offset]; word = (int16_t)(echo * decay); - echo_buffer[echo_buffer_pos++ + echo_buffer_offset] = word; + echo_buffer[echo_buffer_pos++ + echo_buffer_offset] = (int16_t)audiofilters_process_filter_chain(&self->filter, self->base.channel_count, !!echo_buffer_offset, word); } word = (int16_t)(echo * MIN(mix, MICROPY_FLOAT_CONST(1.0))); @@ -409,11 +425,11 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t * for (uint32_t j = echo_buffer_pos >> 8; j < next_buffer_pos >> 8; j++) { word = (int32_t)(echo_buffer[(j % echo_buf_len) + echo_buffer_offset] * decay + sample_word); word = synthio_mix_down_sample(word, SYNTHIO_MIX_DOWN_SCALE(2)); - echo_buffer[(j % echo_buf_len) + echo_buffer_offset] = (int16_t)word; + echo_buffer[(j % echo_buf_len) + echo_buffer_offset] = (int16_t)audiofilters_process_filter_chain(&self->filter, self->base.channel_count, !!echo_buffer_offset, word); } } else { word = synthio_mix_down_sample(word, SYNTHIO_MIX_DOWN_SCALE(2)); - echo_buffer[echo_buffer_pos++ + echo_buffer_offset] = (int16_t)word; + echo_buffer[echo_buffer_pos++ + echo_buffer_offset] = (int16_t)audiofilters_process_filter_chain(&self->filter, self->base.channel_count, !!echo_buffer_offset, word); } } else { if (self->freq_shift) { @@ -421,12 +437,12 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t * word = (int32_t)(echo_buffer[(j % echo_buf_len) + echo_buffer_offset] * decay + sample_word); // Do not have mix_down for 8 bit so just hard cap samples into 1 byte word = MIN(MAX(word, -128), 127); - echo_buffer[(j % echo_buf_len) + echo_buffer_offset] = (int8_t)word; + echo_buffer[(j % echo_buf_len) + echo_buffer_offset] = (int8_t)audiofilters_process_filter_chain(&self->filter, self->base.channel_count, !!echo_buffer_offset, word); } } else { // Do not have mix_down for 8 bit so just hard cap samples into 1 byte word = MIN(MAX(word, -128), 127); - echo_buffer[echo_buffer_pos++ + echo_buffer_offset] = (int8_t)word; + echo_buffer[echo_buffer_pos++ + echo_buffer_offset] = (int8_t)audiofilters_process_filter_chain(&self->filter, self->base.channel_count, !!echo_buffer_offset, word); } } diff --git a/shared-module/audiodelays/Echo.h b/shared-module/audiodelays/Echo.h index cc37f7030be..be01563b85a 100644 --- a/shared-module/audiodelays/Echo.h +++ b/shared-module/audiodelays/Echo.h @@ -8,8 +8,10 @@ #include "py/obj.h" #include "shared-module/audiocore/__init__.h" +#include "shared-module/audiofilters/__init__.h" #include "shared-module/synthio/__init__.h" #include "shared-module/synthio/block.h" +#include "shared-module/synthio/Biquad.h" extern const mp_obj_type_t audiodelays_echo_type; @@ -20,6 +22,7 @@ typedef struct { mp_float_t current_delay_ms; mp_float_t sample_ms; synthio_block_slot_t decay; + audiofilters_filter_chain_t filter; synthio_block_slot_t mix; int8_t *buffer[2]; diff --git a/shared-module/audiodelays/Flanger.c b/shared-module/audiodelays/Flanger.c new file mode 100644 index 00000000000..f0d1bb1e33f --- /dev/null +++ b/shared-module/audiodelays/Flanger.c @@ -0,0 +1,414 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tim Cocks for Adafruit Industries +// +// SPDX-License-Identifier: MIT +#include "shared-bindings/audiodelays/Flanger.h" +#include "shared-bindings/audiocore/__init__.h" + +#include +#include "py/runtime.h" + +// The largest delay we will ever read, in frames. +#define FLANGER_MAX_DELAY_FRAMES (65535u) + +// Convert a delay in milliseconds to a Q16.16 count of frames, clamped so that both interpolation +// taps always land inside the delay line. +static uint32_t flanger_ms_to_frames_q16(audiodelays_flanger_obj_t *self, mp_float_t ms) { + mp_float_t frames = ms * self->base.sample_rate / MICROPY_FLOAT_CONST(1000.0); + mp_float_t max_frames = (mp_float_t)MIN(self->delay_buffer_frames - 2, FLANGER_MAX_DELAY_FRAMES); + + // A delay of less than one frame would read the frame we are about to overwrite + frames = MIN(MAX(frames, MICROPY_FLOAT_CONST(1.0)), max_frames); + + return (uint32_t)(frames * MICROPY_FLOAT_CONST(65536.0)); +} + +void common_hal_audiodelays_flanger_construct(audiodelays_flanger_obj_t *self, uint32_t max_delay_ms, + mp_obj_t min_delay_ms, mp_obj_t rate, mp_obj_t depth, mp_obj_t feedback, mp_obj_t mix, bool invert, + uint32_t buffer_size, uint8_t bits_per_sample, + bool samples_signed, uint8_t channel_count, uint32_t sample_rate) { + + // Basic format settings every effect and audio sample has + self->base.bits_per_sample = bits_per_sample; + self->base.samples_signed = samples_signed; // Are the samples we provide signed (common is true) + self->base.channel_count = channel_count; // Channels can be 1 for mono or 2 for stereo + self->base.sample_rate = sample_rate; // Sample rate for the effect, this generally needs to match all audio objects + self->base.single_buffer = false; + self->base.max_buffer_length = buffer_size; + + // To smooth things out as CircuitPython is doing other tasks most audio objects have a buffer + // A double buffer is set up here so the audio output can use DMA on buffer 1 while we + // write to and create buffer 2. + // This buffer is what is passed to the audio component that plays the effect. + // Samples are set sequentially. For stereo audio they are passed L/R/L/R/... + self->buffer_len = buffer_size; // in bytes + + self->buffer[0] = m_malloc_maybe(self->buffer_len); + if (self->buffer[0] == NULL) { + common_hal_audiodelays_flanger_deinit(self); + m_malloc_fail(self->buffer_len); + } + memset(self->buffer[0], 0, self->buffer_len); + + self->buffer[1] = m_malloc_maybe(self->buffer_len); + if (self->buffer[1] == NULL) { + common_hal_audiodelays_flanger_deinit(self); + m_malloc_fail(self->buffer_len); + } + memset(self->buffer[1], 0, self->buffer_len); + + self->last_buf_idx = 1; // Which buffer to use first, toggle between 0 and 1 + + // Initialize other values most effects will need. + self->sample = NULL; // The current playing sample + self->sample_remaining_buffer = NULL; // Pointer to the start of the sample buffer we have not played + self->sample_buffer_length = 0; // How many samples do we have left to play (these may be 16 bit!) + self->loop = false; // When the sample is done do we loop to the start again or stop (e.g. in a wav file) + self->more_data = false; // Is there still more data to read from the sample or did we finish + + // The below section sets up the flanger effect's starting values. + + // If we did not receive a BlockInput we need to create a default float value + if (min_delay_ms == MP_OBJ_NULL) { + min_delay_ms = mp_obj_new_float(MICROPY_FLOAT_CONST(1.0)); + } + synthio_block_assign_slot(min_delay_ms, &self->min_delay_ms, MP_QSTR_min_delay_ms); + + if (rate == MP_OBJ_NULL) { + rate = mp_obj_new_float(MICROPY_FLOAT_CONST(0.5)); + } + synthio_block_assign_slot(rate, &self->rate, MP_QSTR_rate); + + if (depth == MP_OBJ_NULL) { + depth = mp_obj_new_float(MICROPY_FLOAT_CONST(0.5)); + } + synthio_block_assign_slot(depth, &self->depth, MP_QSTR_depth); + + if (feedback == MP_OBJ_NULL) { + feedback = mp_obj_new_float(MICROPY_FLOAT_CONST(0.5)); + } + synthio_block_assign_slot(feedback, &self->feedback, MP_QSTR_feedback); + + if (mix == MP_OBJ_NULL) { + mix = mp_obj_new_float(MICROPY_FLOAT_CONST(0.5)); + } + synthio_block_assign_slot(mix, &self->mix, MP_QSTR_mix); + + self->invert = invert; + self->max_delay_ms = max_delay_ms; + + // calculate the length of a single sample in milliseconds + self->sample_ms = MICROPY_FLOAT_CONST(1000.0) / self->base.sample_rate; + + // Allocate the delay line for the largest delay we can sweep to. It is always 16-bit and each + // channel gets its own contiguous region. The two extra frames are interpolation headroom: at + // the top of the sweep the two taps sit at the far end of the line and without the extra room + // the older of the two would alias onto the frame we are about to write. + self->delay_buffer_frames = (uint32_t)(self->base.sample_rate / MICROPY_FLOAT_CONST(1000.0) * max_delay_ms) + 2; + size_t delay_buffer_size = self->delay_buffer_frames * self->base.channel_count * sizeof(int16_t); + self->delay_buffer = m_malloc_maybe(delay_buffer_size); + if (self->delay_buffer == NULL) { + common_hal_audiodelays_flanger_deinit(self); + m_malloc_fail(delay_buffer_size); + } + memset(self->delay_buffer, 0, delay_buffer_size); + + self->delay_buffer_pos[0] = self->delay_buffer_pos[1] = 0; + self->lfo_phase[0] = self->lfo_phase[1] = 0; + self->lfo_phase_inc = 0; +} + +bool common_hal_audiodelays_flanger_deinited(audiodelays_flanger_obj_t *self) { + if (self->delay_buffer == NULL) { + return true; + } + return false; +} + +void common_hal_audiodelays_flanger_deinit(audiodelays_flanger_obj_t *self) { + audiosample_mark_deinit(&self->base); + self->delay_buffer = NULL; + self->buffer[0] = NULL; + self->buffer[1] = NULL; +} + +mp_obj_t common_hal_audiodelays_flanger_get_min_delay_ms(audiodelays_flanger_obj_t *self) { + return self->min_delay_ms.obj; +} + +void common_hal_audiodelays_flanger_set_min_delay_ms(audiodelays_flanger_obj_t *self, mp_obj_t min_delay_ms) { + synthio_block_assign_slot(min_delay_ms, &self->min_delay_ms, MP_QSTR_min_delay_ms); +} + +mp_obj_t common_hal_audiodelays_flanger_get_rate(audiodelays_flanger_obj_t *self) { + return self->rate.obj; +} + +void common_hal_audiodelays_flanger_set_rate(audiodelays_flanger_obj_t *self, mp_obj_t rate) { + synthio_block_assign_slot(rate, &self->rate, MP_QSTR_rate); +} + +mp_obj_t common_hal_audiodelays_flanger_get_depth(audiodelays_flanger_obj_t *self) { + return self->depth.obj; +} + +void common_hal_audiodelays_flanger_set_depth(audiodelays_flanger_obj_t *self, mp_obj_t depth) { + synthio_block_assign_slot(depth, &self->depth, MP_QSTR_depth); +} + +mp_obj_t common_hal_audiodelays_flanger_get_feedback(audiodelays_flanger_obj_t *self) { + return self->feedback.obj; +} + +void common_hal_audiodelays_flanger_set_feedback(audiodelays_flanger_obj_t *self, mp_obj_t feedback) { + synthio_block_assign_slot(feedback, &self->feedback, MP_QSTR_feedback); +} + +mp_obj_t common_hal_audiodelays_flanger_get_mix(audiodelays_flanger_obj_t *self) { + return self->mix.obj; +} + +void common_hal_audiodelays_flanger_set_mix(audiodelays_flanger_obj_t *self, mp_obj_t arg) { + synthio_block_assign_slot(arg, &self->mix, MP_QSTR_mix); +} + +// Fold the sawtooth phase accumulator into a triangle in the range 0 to 65535 +static uint32_t flanger_phase_to_triangle(uint32_t phase) { + uint32_t tri = phase >> 15; + if (tri > 65535) { + tri = 131071 - tri; + } + return tri; +} + +mp_float_t common_hal_audiodelays_flanger_get_lfo_value(audiodelays_flanger_obj_t *self) { + return (mp_float_t)flanger_phase_to_triangle(self->lfo_phase[0]) / MICROPY_FLOAT_CONST(65535.0); +} + +bool common_hal_audiodelays_flanger_get_invert(audiodelays_flanger_obj_t *self) { + return self->invert; +} + +void common_hal_audiodelays_flanger_set_invert(audiodelays_flanger_obj_t *self, bool invert) { + self->invert = invert; +} + +void audiodelays_flanger_reset_buffer(audiodelays_flanger_obj_t *self, + bool single_channel_output, + uint8_t channel) { + + memset(self->buffer[0], 0, self->buffer_len); + memset(self->buffer[1], 0, self->buffer_len); + memset(self->delay_buffer, 0, self->delay_buffer_frames * self->base.channel_count * sizeof(int16_t)); + self->delay_buffer_pos[0] = self->delay_buffer_pos[1] = 0; + self->lfo_phase[0] = self->lfo_phase[1] = 0; +} + +bool common_hal_audiodelays_flanger_get_playing(audiodelays_flanger_obj_t *self) { + return self->sample != NULL; +} + +void common_hal_audiodelays_flanger_play(audiodelays_flanger_obj_t *self, mp_obj_t sample, bool loop) { + audiosample_must_match(&self->base, sample, false); + + self->sample = sample; + self->loop = loop; + + audiosample_reset_buffer(self->sample, false, 0); + audioio_get_buffer_result_t result = audiosample_get_buffer(self->sample, false, 0, (uint8_t **)&self->sample_remaining_buffer, &self->sample_buffer_length); + + // Track remaining sample length in terms of bytes per sample + self->sample_buffer_length /= (self->base.bits_per_sample / 8); + // Store if we have more data in the sample to retrieve + self->more_data = result == GET_BUFFER_MORE_DATA; +} + +void common_hal_audiodelays_flanger_stop(audiodelays_flanger_obj_t *self) { + self->sample = NULL; +} + +audioio_get_buffer_result_t audiodelays_flanger_get_buffer(audiodelays_flanger_obj_t *self, bool single_channel_output, uint8_t channel, + uint8_t **buffer, uint32_t *buffer_length) { + + if (!single_channel_output) { + channel = 0; + } + + // Switch our buffers to the other buffer + self->last_buf_idx = !self->last_buf_idx; + + // If we are using 16 bit samples we need a 16 bit pointer, 8 bit needs an 8 bit pointer + int16_t *word_buffer = (int16_t *)self->buffer[self->last_buf_idx]; + int8_t *hword_buffer = self->buffer[self->last_buf_idx]; + uint32_t length = self->buffer_len / (self->base.bits_per_sample / 8); + + uint8_t channel_count = self->base.channel_count; + uint32_t frames = self->delay_buffer_frames; + + // For single channel output every word in this call belongs to `channel`. Otherwise the + // channels are interleaved and alternate with every word. + bool single_right = single_channel_output && channel == 1 && channel_count == 2; + + // Loop over the entire length of our buffer to fill it, this may require several calls to get data from the sample + while (length != 0) { + // Check if there is no more sample to play, we will either load more data, reset the sample if loop is on or clear the sample + if (self->sample_buffer_length == 0) { + if (!self->more_data) { // The sample has indicated it has no more data to play + if (self->loop && self->sample) { // If we are supposed to loop reset the sample to the start + audiosample_reset_buffer(self->sample, false, 0); + } else { // If we were not supposed to loop the sample, stop playing it + self->sample = NULL; + } + } + if (self->sample) { + // Load another sample buffer to play + audioio_get_buffer_result_t result = audiosample_get_buffer(self->sample, false, 0, (uint8_t **)&self->sample_remaining_buffer, &self->sample_buffer_length); + // Track length in terms of words. + self->sample_buffer_length /= (self->base.bits_per_sample / 8); + self->more_data = result == GET_BUFFER_MORE_DATA; + } + } + + if (self->sample == NULL) { + // tick all block inputs so that anything attached to them stays in sync + shared_bindings_synthio_lfo_tick(self->base.sample_rate, length / channel_count); + (void)synthio_block_slot_get(&self->min_delay_ms); + (void)synthio_block_slot_get(&self->rate); + (void)synthio_block_slot_get(&self->depth); + (void)synthio_block_slot_get(&self->feedback); + (void)synthio_block_slot_get(&self->mix); + + if (self->base.samples_signed) { + memset(word_buffer, 0, length * (self->base.bits_per_sample / 8)); + } else { + // For unsigned samples set to the middle which is "quiet" + if (MP_LIKELY(self->base.bits_per_sample == 16)) { + uint16_t *uword_buffer = (uint16_t *)word_buffer; + while (length--) { + *uword_buffer++ = 32768; + } + } else { + memset(hword_buffer, 128, length * (self->base.bits_per_sample / 8)); + } + } + + length = 0; + } else { + // we have a sample to play and flange + // Determine how many bytes we can process to our buffer, the less of the sample we have left and our buffer remaining + uint32_t num_bytes = MIN(MIN(self->sample_buffer_length, length), SYNTHIO_MAX_DUR * channel_count); + + int16_t *sample_src = (int16_t *)self->sample_remaining_buffer; // for 16-bit samples + int8_t *sample_hsrc = (int8_t *)self->sample_remaining_buffer; // for 8-bit samples + + // get the effect values we need from the BlockInput + shared_bindings_synthio_lfo_tick(self->base.sample_rate, num_bytes / channel_count); + mp_float_t f_min_delay_ms = synthio_block_slot_get_limited(&self->min_delay_ms, self->sample_ms, (mp_float_t)self->max_delay_ms); + mp_float_t f_rate = synthio_block_slot_get_limited(&self->rate, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(20.0)); + mp_float_t f_depth = synthio_block_slot_get_limited(&self->depth, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(1.0)); + int32_t feedback = (int32_t)(synthio_block_slot_get_limited(&self->feedback, MICROPY_FLOAT_CONST(-0.95), MICROPY_FLOAT_CONST(0.95)) * 32767); + int32_t mix = (int32_t)(synthio_block_slot_get_limited(&self->mix, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(1.0)) * 32767); + + // The sweep runs upward from min_delay_ms towards max_delay_ms rather than around a + // centre, so no combination of min_delay_ms and depth can push it out of the delay line. + mp_float_t sweep_top_ms = f_min_delay_ms + f_depth * ((mp_float_t)self->max_delay_ms - f_min_delay_ms); + uint32_t delay_min_q16 = flanger_ms_to_frames_q16(self, f_min_delay_ms); + uint32_t delay_span_q16 = flanger_ms_to_frames_q16(self, sweep_top_ms) - delay_min_q16; + + // How far the LFO advances each frame. A rate at or above half the sample rate is + // meaningless, and clamping there also keeps the conversion inside a uint32_t. + mp_float_t phase_inc = MIN(f_rate / self->base.sample_rate, MICROPY_FLOAT_CONST(0.5)); + self->lfo_phase_inc = (uint32_t)(phase_inc * MICROPY_FLOAT_CONST(4294967296.0)); + + for (uint32_t i = 0; i < num_bytes; i++) { + bool right_channel = single_channel_output ? single_right : ((i % channel_count) == 1); + + int32_t sample_word = 0; + if (MP_LIKELY(self->base.bits_per_sample == 16)) { + sample_word = sample_src[i]; + } else { + if (self->base.samples_signed) { + sample_word = sample_hsrc[i]; + } else { + // changing from an 8 bit unsigned to signed into a 32-bit signed + sample_word = (int8_t)(((uint8_t)sample_hsrc[i]) ^ 0x80); + } + } + + // Advance this channel's LFO by one frame. Each channel has its own accumulator so + // that the sweep runs at `rate` whether the channels arrive interleaved (both + // accumulators step once per frame) or one channel per call. + self->lfo_phase[right_channel] += self->lfo_phase_inc; + + uint32_t tri = flanger_phase_to_triangle(self->lfo_phase[right_channel]); + + // The delay for this frame, in Q16.16 frames + uint32_t delay_q16 = delay_min_q16 + (uint32_t)(((uint64_t)delay_span_q16 * tri) >> 16); + uint32_t delay_int = delay_q16 >> 16; + uint32_t delay_frac = delay_q16 & 0xffff; + + int16_t *line = self->delay_buffer + (right_channel ? frames : 0); + uint32_t w = self->delay_buffer_pos[right_channel]; + + // Two taps straddling the fractional delay, counted back from the write head. + // delay_int is at most frames - 2 so neither index can escape the region. + uint32_t r0 = w + frames - delay_int; + if (r0 >= frames) { + r0 -= frames; + } + uint32_t r1 = r0 ? r0 - 1 : frames - 1; + + // Linear interpolation between them + int32_t s0 = line[r0]; + int32_t s1 = line[r1]; + int32_t wet = s0 + (((s1 - s0) * (int32_t)delay_frac) >> 16); + + // Write the input plus the regenerated signal back into the line + line[w] = synthio_sat16(sample_word + synthio_sat16(wet * feedback, 15), 0); + self->delay_buffer_pos[right_channel] = (w + 1 == frames) ? 0 : w + 1; + + int32_t word; + if (mix <= 328) { // if mix is zero (0.01 in fixed point), pure sample only + // Note that we still ran the delay line above. + word = sample_word; + } else { + int32_t wet_word = synthio_sat16(wet * mix, 15); + // Add (or, inverted, subtract) original sample + effect + word = self->invert ? sample_word - wet_word : sample_word + wet_word; + word = synthio_mix_down_sample(word, SYNTHIO_MIX_DOWN_SCALE(2)); + } + + if (MP_LIKELY(self->base.bits_per_sample == 16)) { + word_buffer[i] = word; + if (!self->base.samples_signed) { + word_buffer[i] ^= 0x8000; + } + } else { + // 8-bit samples have no headroom for synthio_mix_down_sample to work with, so + // clamp instead or the sum of the dry and wet signals wraps around + int8_t out = MIN(MAX(word, -128), 127); + if (self->base.samples_signed) { + hword_buffer[i] = out; + } else { + hword_buffer[i] = (uint8_t)out ^ 0x80; + } + } + } + + // Update the remaining length and the buffer positions based on how much we wrote into our buffer + length -= num_bytes; + word_buffer += num_bytes; + hword_buffer += num_bytes; + self->sample_remaining_buffer += (num_bytes * (self->base.bits_per_sample / 8)); + self->sample_buffer_length -= num_bytes; + } + } + + // Finally pass our buffer and length to the calling audio function + *buffer = (uint8_t *)self->buffer[self->last_buf_idx]; + *buffer_length = self->buffer_len; + + // Flanger always returns more data but some effects may return GET_BUFFER_DONE or GET_BUFFER_ERROR (see audiocore/__init__.h) + return GET_BUFFER_MORE_DATA; +} diff --git a/shared-module/audiodelays/Flanger.h b/shared-module/audiodelays/Flanger.h new file mode 100644 index 00000000000..6e8a37f51e2 --- /dev/null +++ b/shared-module/audiodelays/Flanger.h @@ -0,0 +1,62 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tim Cocks for Adafruit Industries +// +// SPDX-License-Identifier: MIT +#pragma once + +#include "py/obj.h" + +#include "shared-module/audiocore/__init__.h" +#include "shared-module/synthio/block.h" + +extern const mp_obj_type_t audiodelays_flanger_type; + +typedef struct { + audiosample_base_t base; + + // Effect parameters. + synthio_block_slot_t min_delay_ms; // floor of the sweep + synthio_block_slot_t rate; // LFO frequency in Hz + synthio_block_slot_t depth; // 0 to 1, portion of the available range swept + synthio_block_slot_t feedback; // -0.95 to 0.95, regeneration + synthio_block_slot_t mix; // 0 to 1, dry to wet + + uint32_t max_delay_ms; + mp_float_t sample_ms; // length of a single sample in milliseconds + + bool invert; // subtract the wet signal instead of adding it + + int8_t *buffer[2]; + uint8_t last_buf_idx; + uint32_t buffer_len; // max buffer in bytes + + uint8_t *sample_remaining_buffer; + uint32_t sample_buffer_length; + + bool loop; + bool more_data; + + // The delay line is always 16-bit and is split into one contiguous region per channel so + // that read/write positions are plain frame counts and wrapping is a single modulo. + int16_t *delay_buffer; + uint32_t delay_buffer_frames; // frames in each per channel region + uint32_t delay_buffer_pos[2]; // write position in frames, per channel + + // Internal LFO. One phase accumulator per channel so that the sweep advances once per frame + // whether the channels are interleaved in one call or requested one channel at a time. + uint32_t lfo_phase[2]; // Q0.32, wraps naturally + uint32_t lfo_phase_inc; // Q0.32 step per frame, recalculated per chunk from rate + + mp_obj_t sample; +} audiodelays_flanger_obj_t; + +void audiodelays_flanger_reset_buffer(audiodelays_flanger_obj_t *self, + bool single_channel_output, + uint8_t channel); + +audioio_get_buffer_result_t audiodelays_flanger_get_buffer(audiodelays_flanger_obj_t *self, + bool single_channel_output, + uint8_t channel, + uint8_t **buffer, + uint32_t *buffer_length); // length in bytes diff --git a/shared-module/audiofilters/Filter.c b/shared-module/audiofilters/Filter.c index 57a763629fa..a5351edecc4 100644 --- a/shared-module/audiofilters/Filter.c +++ b/shared-module/audiofilters/Filter.c @@ -5,8 +5,8 @@ // SPDX-License-Identifier: MIT #include "shared-bindings/audiofilters/Filter.h" #include "shared-bindings/audiocore/__init__.h" - #include "shared-module/synthio/Biquad.h" + #include #include "py/runtime.h" @@ -59,60 +59,17 @@ void common_hal_audiofilters_filter_construct(audiofilters_filter_obj_t *self, void common_hal_audiofilters_filter_deinit(audiofilters_filter_obj_t *self) { audiosample_mark_deinit(&self->base); + audiofilters_deinit_filter_chain(&self->filter); self->buffer[0] = NULL; self->buffer[1] = NULL; - self->filter = mp_const_none; - self->filter_buffer = NULL; - self->filter_states = NULL; } void common_hal_audiofilters_filter_set_filter(audiofilters_filter_obj_t *self, mp_obj_t filter_in) { - size_t n_items; - mp_obj_t *items; - mp_obj_t *filter_objs; - - if (filter_in == mp_const_none) { - n_items = 0; - filter_objs = NULL; - } else if (MP_OBJ_TYPE_HAS_SLOT(mp_obj_get_type(filter_in), iter)) { - // convert object to tuple if it wasn't before - filter_in = MP_OBJ_TYPE_GET_SLOT(&mp_type_tuple, make_new)( - &mp_type_tuple, 1, 0, &filter_in); - mp_obj_tuple_get(filter_in, &n_items, &items); - for (size_t i = 0; i < n_items; i++) { - if (!mp_obj_is_type(items[i], &synthio_biquad_type_obj)) { - mp_raise_TypeError_varg( - MP_ERROR_TEXT("%q in %q must be of type %q, not %q"), - MP_QSTR_object, - MP_QSTR_filter, - MP_QSTR_Biquad, - mp_obj_get_type(items[i])->name); - } - } - filter_objs = items; - } else { - n_items = 1; - if (!mp_obj_is_type(filter_in, &synthio_biquad_type_obj)) { - mp_raise_TypeError_varg( - MP_ERROR_TEXT("%q must be of type %q or %q, not %q"), - MP_QSTR_filter, MP_QSTR_Biquad, MP_QSTR_iterable, mp_obj_get_type(filter_in)->name); - } - filter_objs = &self->filter; - } - - // everything has been checked, so we can do the following without fear - - self->filter = filter_in; - self->filter_objs = filter_objs; - self->filter_states = m_renew(biquad_filter_state, - self->filter_states, - self->filter_objs_len * self->base.channel_count, - n_items * self->base.channel_count); - self->filter_objs_len = n_items; + audiofilters_assign_filter_chain(&self->filter, filter_in, self->base.channel_count); } mp_obj_t common_hal_audiofilters_filter_get_filter(audiofilters_filter_obj_t *self) { - return self->filter; + return self->filter.obj; } mp_obj_t common_hal_audiofilters_filter_get_mix(audiofilters_filter_obj_t *self) { @@ -131,11 +88,7 @@ void audiofilters_filter_reset_buffer(audiofilters_filter_obj_t *self, memset(self->buffer[1], 0, self->buffer_len); memset(self->filter_buffer, 0, SYNTHIO_MAX_DUR * self->base.channel_count * sizeof(int32_t)); - if (self->filter_states) { - for (uint8_t i = 0; i < self->filter_objs_len * self->base.channel_count; i++) { - synthio_biquad_filter_reset(&self->filter_states[i]); - } - } + audiofilters_reset_filter_chain(&self->filter, self->base.channel_count); } bool common_hal_audiofilters_filter_get_playing(audiofilters_filter_obj_t *self) { @@ -213,9 +166,8 @@ audioio_get_buffer_result_t audiofilters_filter_get_buffer(audiofilters_filter_o (void)synthio_block_slot_get(&self->mix); // Tick biquad filters - for (uint8_t j = 0; j < self->filter_objs_len; j++) { - common_hal_synthio_biquad_tick(self->filter_objs[j]); - } + audiofilters_tick_filter_chain(&self->filter); + if (self->base.samples_signed) { memset(word_buffer, 0, length * (self->base.bits_per_sample / 8)); } else { @@ -243,7 +195,7 @@ audioio_get_buffer_result_t audiofilters_filter_get_buffer(audiofilters_filter_o shared_bindings_synthio_lfo_tick(self->base.sample_rate, n / self->base.channel_count); mp_float_t mix = synthio_block_slot_get_limited(&self->mix, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(1.0)); - if (mix <= MICROPY_FLOAT_CONST(0.01) || !self->filter_states) { // if mix is zero pure sample only or no biquad filter objects are provided + if (mix <= MICROPY_FLOAT_CONST(0.01) || !self->filter.states) { // if mix is zero pure sample only or no biquad filter objects are provided for (uint32_t i = 0; i < n; i++) { if (MP_LIKELY(self->base.bits_per_sample == 16)) { word_buffer[i] = sample_src[i]; @@ -273,11 +225,11 @@ audioio_get_buffer_result_t audiofilters_filter_get_buffer(audiofilters_filter_o } // Process biquad filters - for (uint8_t j = 0; j < self->filter_objs_len; j++) { - mp_obj_t filter_obj = self->filter_objs[j]; + for (uint8_t j = 0; j < self->filter.objs_len; j++) { + mp_obj_t filter_obj = self->filter.objs[j]; common_hal_synthio_biquad_tick(filter_obj); for (uint8_t k = 0; k < self->base.channel_count; k++) { - synthio_biquad_filter_samples(filter_obj, &self->filter_states[j * self->base.channel_count + k], self->filter_buffer + k * SYNTHIO_MAX_DUR, n_samples); + synthio_biquad_filter_samples(filter_obj, &self->filter.states[j * self->base.channel_count + k], self->filter_buffer + k * SYNTHIO_MAX_DUR, n_samples); } } diff --git a/shared-module/audiofilters/Filter.h b/shared-module/audiofilters/Filter.h index e24827e44a8..6cb352d4273 100644 --- a/shared-module/audiofilters/Filter.h +++ b/shared-module/audiofilters/Filter.h @@ -9,6 +9,7 @@ #include "shared-bindings/synthio/Biquad.h" #include "shared-module/audiocore/__init__.h" +#include "shared-module/audiofilters/__init__.h" #include "shared-module/synthio/__init__.h" #include "shared-module/synthio/block.h" #include "shared-module/synthio/Biquad.h" @@ -17,13 +18,9 @@ extern const mp_obj_type_t audiofilters_filter_type; typedef struct { audiosample_base_t base; - mp_obj_t filter; + audiofilters_filter_chain_t filter; synthio_block_slot_t mix; - mp_obj_t *filter_objs; - size_t filter_objs_len; - biquad_filter_state *filter_states; - int8_t *buffer[2]; uint8_t last_buf_idx; uint32_t buffer_len; // max buffer in bytes diff --git a/shared-module/audiofilters/__init__.c b/shared-module/audiofilters/__init__.c index 83929b4c4fb..4b2ec0ed063 100644 --- a/shared-module/audiofilters/__init__.c +++ b/shared-module/audiofilters/__init__.c @@ -3,3 +3,74 @@ // SPDX-FileCopyrightText: Copyright (c) 2024 Cooper Dalrymple // // SPDX-License-Identifier: MIT + +#include "shared-module/audiofilters/__init__.h" + +void audiofilters_assign_filter_chain(audiofilters_filter_chain_t *self, mp_obj_t filter_in, uint8_t channel_count) { + size_t n_items; + mp_obj_t *items; + + if (filter_in == mp_const_none) { + n_items = 0; + items = NULL; + } else if (mp_obj_is_type(filter_in, &mp_type_tuple)) { + mp_obj_tuple_get(filter_in, &n_items, &items); + for (size_t i = 0; i < n_items; i++) { + if (!mp_obj_is_type(items[i], &synthio_biquad_type_obj)) { + mp_raise_TypeError_varg( + MP_ERROR_TEXT("%q in %q must be of type %q, not %q"), + MP_QSTR_object, + MP_QSTR_filter, + MP_QSTR_Biquad, + mp_obj_get_type(items[i])->name); + } + } + } else { + n_items = 1; + if (!mp_obj_is_type(filter_in, &synthio_biquad_type_obj)) { + mp_raise_TypeError_varg( + MP_ERROR_TEXT("%q must be of type %q or %q, not %q"), + MP_QSTR_filter, MP_QSTR_Biquad, MP_QSTR_tuple, mp_obj_get_type(filter_in)->name); + } + items = &self->obj; + } + + // everything has been checked, so we can do the following without fear + + self->obj = filter_in; + self->states = m_renew(biquad_filter_state, + self->states, + self->objs_len * channel_count, + n_items * channel_count); + self->objs = items; + self->objs_len = n_items; +} + +void audiofilters_reset_filter_chain(audiofilters_filter_chain_t *self, uint8_t channel_count) { + if (self->states) { + for (uint8_t i = 0; i < self->objs_len * channel_count; i++) { + synthio_biquad_filter_reset(&self->states[i]); + } + } +} + +void audiofilters_tick_filter_chain(audiofilters_filter_chain_t *self) { + for (uint8_t j = 0; j < self->objs_len; j++) { + common_hal_synthio_biquad_tick(self->objs[j]); + } +} + +int32_t audiofilters_process_filter_chain(audiofilters_filter_chain_t *self, uint8_t channel_count, uint8_t channel, int32_t word) { + // Process biquad filters + for (uint8_t j = 0; j < self->objs_len; j++) { + word = synthio_biquad_filter_sample(self->objs[j], &self->states[j * channel_count + channel], word); + } + return word; +} + +void audiofilters_deinit_filter_chain(audiofilters_filter_chain_t *self) { + self->obj = mp_const_none; + self->objs = NULL; + self->objs_len = 0; + self->states = NULL; +} diff --git a/shared-module/audiofilters/__init__.h b/shared-module/audiofilters/__init__.h index 29d2f672655..2a5a933d96f 100644 --- a/shared-module/audiofilters/__init__.h +++ b/shared-module/audiofilters/__init__.h @@ -5,3 +5,19 @@ // SPDX-License-Identifier: MIT #pragma once + +#include "py/obj.h" +#include "shared-module/synthio/Biquad.h" + +typedef struct audiofilters_filter_chain { + mp_obj_t obj; + mp_obj_t *objs; + size_t objs_len; + biquad_filter_state *states; +} audiofilters_filter_chain_t; + +void audiofilters_assign_filter_chain(audiofilters_filter_chain_t *filter_chain, mp_obj_t filter_in, uint8_t channel_count); +void audiofilters_reset_filter_chain(audiofilters_filter_chain_t *filter_chain, uint8_t channel_count); +void audiofilters_tick_filter_chain(audiofilters_filter_chain_t *filter_chain); +int32_t audiofilters_process_filter_chain(audiofilters_filter_chain_t *filter_chain, uint8_t channel_count, uint8_t channel, int32_t word); +void audiofilters_deinit_filter_chain(audiofilters_filter_chain_t *self); diff --git a/shared-module/audiofreeverb/Freeverb.c b/shared-module/audiofreeverb/Freeverb.c index d0f1aeaa546..85aefb6fc61 100644 --- a/shared-module/audiofreeverb/Freeverb.c +++ b/shared-module/audiofreeverb/Freeverb.c @@ -15,7 +15,7 @@ #include "py/runtime.h" #include -void common_hal_audiofreeverb_freeverb_construct(audiofreeverb_freeverb_obj_t *self, mp_obj_t roomsize, mp_obj_t damp, mp_obj_t mix, +void common_hal_audiofreeverb_freeverb_construct(audiofreeverb_freeverb_obj_t *self, mp_obj_t roomsize, mp_obj_t damp, mp_obj_t pre_filter, mp_obj_t post_filter, mp_obj_t mix, uint32_t buffer_size, uint8_t bits_per_sample, bool samples_signed, uint8_t channel_count, uint32_t sample_rate) { @@ -59,6 +59,9 @@ void common_hal_audiofreeverb_freeverb_construct(audiofreeverb_freeverb_obj_t *s self->more_data = false; // Is there still more data to read from the sample or did we finish // The below section sets up the reverb effect's starting values. For a different effect this section will change + common_hal_audiofreeverb_freeverb_set_pre_filter(self, pre_filter); + common_hal_audiofreeverb_freeverb_set_post_filter(self, post_filter); + if (roomsize == MP_OBJ_NULL) { roomsize = mp_obj_new_float(MICROPY_FLOAT_CONST(0.5)); } @@ -126,6 +129,8 @@ bool common_hal_audiofreeverb_freeverb_deinited(audiofreeverb_freeverb_obj_t *se void common_hal_audiofreeverb_freeverb_deinit(audiofreeverb_freeverb_obj_t *self) { audiosample_mark_deinit(&self->base); + audiofilters_deinit_filter_chain(&self->pre_filter); + audiofilters_deinit_filter_chain(&self->post_filter); self->buffer[0] = NULL; self->buffer[1] = NULL; } @@ -167,6 +172,22 @@ void audiofreeverb_freeverb_get_damp_fixedpoint(mp_float_t n, int16_t *damp1, in *damp2 = (int16_t)(32768 - *damp1); // inverse of x1 damp2 = 1.0 - damp1 } +mp_obj_t common_hal_audiofreeverb_freeverb_get_pre_filter(audiofreeverb_freeverb_obj_t *self) { + return self->pre_filter.obj; +} + +void common_hal_audiofreeverb_freeverb_set_pre_filter(audiofreeverb_freeverb_obj_t *self, mp_obj_t filter_in) { + audiofilters_assign_filter_chain(&self->pre_filter, filter_in, self->base.channel_count); +} + +mp_obj_t common_hal_audiofreeverb_freeverb_get_post_filter(audiofreeverb_freeverb_obj_t *self) { + return self->post_filter.obj; +} + +void common_hal_audiofreeverb_freeverb_set_post_filter(audiofreeverb_freeverb_obj_t *self, mp_obj_t filter_in) { + audiofilters_assign_filter_chain(&self->post_filter, filter_in, self->base.channel_count); +} + mp_obj_t common_hal_audiofreeverb_freeverb_get_mix(audiofreeverb_freeverb_obj_t *self) { return self->mix.obj; } @@ -187,6 +208,9 @@ void audiofreeverb_freeverb_reset_buffer(audiofreeverb_freeverb_obj_t *self, memset(self->buffer[0], 0, self->buffer_len); memset(self->buffer[1], 0, self->buffer_len); + + audiofilters_reset_filter_chain(&self->pre_filter, self->base.channel_count); + audiofilters_reset_filter_chain(&self->post_filter, self->base.channel_count); } bool common_hal_audiofreeverb_freeverb_get_playing(audiofreeverb_freeverb_obj_t *self) { @@ -274,6 +298,10 @@ audioio_get_buffer_result_t audiofreeverb_freeverb_get_buffer(audiofreeverb_free mp_float_t roomsize = synthio_block_slot_get_limited(&self->roomsize, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(1.0)); int16_t feedback = audiofreeverb_freeverb_get_roomsize_fixedpoint(roomsize); + // Tick biquad filters + audiofilters_tick_filter_chain(&self->pre_filter); + audiofilters_tick_filter_chain(&self->post_filter); + int16_t *sample_src = (int16_t *)self->sample_remaining_buffer; for (uint32_t i = 0; i < n; i++) { @@ -286,7 +314,10 @@ audioio_get_buffer_result_t audiofreeverb_freeverb_get_buffer(audiofreeverb_free int16_t input, bufout, output; uint32_t channel_comb_offset = 0, channel_allpass_offset = 0; - input = synthio_sat16(sample_word * 8738, 17); // Initial input scaled down so we can add reverb + // Apply filters as Pre-EQ + input = (int16_t)audiofilters_process_filter_chain(&self->pre_filter, self->base.channel_count, n % self->base.channel_count, sample_word); + + input = synthio_sat16((int32_t)input * 8738, 17); // Initial input scaled down so we can add reverb sum = 0; // Calculate each of the 8 comb buffers @@ -312,6 +343,9 @@ audioio_get_buffer_result_t audiofreeverb_freeverb_get_buffer(audiofreeverb_free } } + // Apply filters as Post-EQ + output = (int16_t)audiofilters_process_filter_chain(&self->post_filter, self->base.channel_count, n % self->base.channel_count, (int32_t)output); + word = output * 30; // Add some volume back don't have to saturate as next step will word = synthio_sat16(sample_word * mix_sample, 15) + synthio_sat16(word * mix_effect, 15); diff --git a/shared-module/audiofreeverb/Freeverb.h b/shared-module/audiofreeverb/Freeverb.h index 44747f0fc95..1a60a64d39e 100644 --- a/shared-module/audiofreeverb/Freeverb.h +++ b/shared-module/audiofreeverb/Freeverb.h @@ -8,8 +8,10 @@ #include "py/obj.h" #include "shared-module/audiocore/__init__.h" +#include "shared-module/audiofilters/__init__.h" #include "shared-module/synthio/__init__.h" #include "shared-module/synthio/block.h" +#include "shared-module/synthio/Biquad.h" extern const mp_obj_type_t audiofreeverb_freeverb_type; @@ -17,6 +19,8 @@ typedef struct { audiosample_base_t base; synthio_block_slot_t roomsize; synthio_block_slot_t damp; + audiofilters_filter_chain_t pre_filter; + audiofilters_filter_chain_t post_filter; synthio_block_slot_t mix; int8_t *buffer[2]; diff --git a/shared-module/audiomixer/Mixer.c b/shared-module/audiomixer/Mixer.c index f3111b8dc6f..a6c4397e8bf 100644 --- a/shared-module/audiomixer/Mixer.c +++ b/shared-module/audiomixer/Mixer.c @@ -66,7 +66,7 @@ void audiomixer_mixer_reset_buffer(audiomixer_mixer_obj_t *self, bool single_channel_output, uint8_t channel) { for (uint8_t i = 0; i < self->voice_count; i++) { - common_hal_audiomixer_mixervoice_stop(self->voice[i]); + common_hal_audiomixer_mixervoice_reset_buffer(self->voice[i]); } } @@ -173,6 +173,33 @@ static inline uint32_t copy16msb(uint32_t val) { #endif } +// Rather than immediately changing the loudness of audio playback, we keep a separate buffer of +// the "active" loudness and wait until we meet the conditions of a "zero crossing". A zero crossing +// occurs when either the current value is 0 or the value changes from negative to positive or +// vice-versa. This is detected by keeping a copy of the previous frame of audio data and checking +// to see if the sign of the value has changed. By only changing loudness during zero crossings, we +// avoid audible pops/clicks which can be unpleasant. +static void assignmul(uint32_t word, uint32_t *last_word, int32_t *active_lomul, int32_t *active_himul, int32_t pending_lomul, int32_t pending_himul) { + // If the active loudness already matches the pending loudness, exit early. + if (MP_LIKELY(*active_lomul == pending_lomul) && MP_LIKELY(*active_himul == pending_himul)) { + return; + } + + // Check for a zero crossing: current value is 0 or has changed sign from the previous value. + // We check for the sign change by bitmasking only the top bits of each 16-bit signed value + // packed into the 32-bit word (two's complement). If either bit doesn't match the previous + // value, a sign change has occurred on either the left or right channel. + if ((word & 0xffff0000) == 0 || (word & 0x0000ffff) == 0 || (*last_word != 0 && ((*last_word) & 0x80008000) != (word & 0x80008000))) { + // Copy over our pending loudness. Will cause future calls to `assignmul` to exit early. + *active_lomul = pending_lomul; + *active_himul = pending_himul; + } else { + // Update our copy of the previous word for future comparisons (an initial value of 0 is + // ignored). + *last_word = word; + } +} + #define ALMOST_ONE (MICROPY_FLOAT_CONST(32767.) / 32768) static void mix_down_one_voice(audiomixer_mixer_obj_t *self, @@ -238,42 +265,56 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, } } - int32_t lo_level = level; - int32_t hi_level = level; + int32_t pending_lo_level = level; + int32_t pending_hi_level = level; if (MP_LIKELY(self->base.channel_count == 2)) { - lo_level = (left_panning_scaled * lo_level) >> 15; - hi_level = (right_panning_scaled * hi_level) >> 15; + pending_lo_level = (left_panning_scaled * pending_lo_level) >> 15; + pending_hi_level = (right_panning_scaled * pending_hi_level) >> 15; } + int32_t active_lo_level = voice->active_lo_level; + int32_t active_hi_level = voice->active_hi_level; + uint32_t last_word = 0; + // First active voice gets copied over verbatim. if (!voices_active) { if (MP_LIKELY(self->base.bits_per_sample == 16)) { if (MP_LIKELY(self->base.samples_signed)) { if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { for (uint32_t i = 0; i < n; i++) { - uint32_t v = src[i]; - word_buffer[i] = mult16signed(v, lo_level, hi_level); + uint32_t word = src[i]; + assignmul(word, &last_word, &active_lo_level, &active_hi_level, pending_lo_level, pending_hi_level); + word_buffer[i] = mult16signed(word, active_lo_level, active_hi_level); } } else { for (uint32_t i = 0; i < n; i += 2) { - uint32_t v = src[i >> 1]; - word_buffer[i] = mult16signed(copy16lsb(v), lo_level, hi_level); - word_buffer[i + 1] = mult16signed(copy16msb(v), lo_level, hi_level); + uint32_t word = src[i >> 1]; + uint32_t word_lsb = copy16lsb(word); + assignmul(word_lsb, &last_word, &active_lo_level, &active_hi_level, pending_lo_level, pending_hi_level); + word_buffer[i] = mult16signed(word_lsb, active_lo_level, active_hi_level); + word = copy16msb(word); + assignmul(word, &last_word, &active_lo_level, &active_hi_level, pending_lo_level, pending_hi_level); + word_buffer[i + 1] = mult16signed(word, active_lo_level, active_hi_level); } } } else { if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { for (uint32_t i = 0; i < n; i++) { - uint32_t v = src[i]; - v = tosigned16(v); - word_buffer[i] = mult16signed(v, lo_level, hi_level); + uint32_t word = src[i]; + word = tosigned16(word); + assignmul(word, &last_word, &active_lo_level, &active_hi_level, pending_lo_level, pending_hi_level); + word_buffer[i] = mult16signed(word, active_lo_level, active_hi_level); } } else { for (uint32_t i = 0; i + 1 < n; i += 2) { - uint32_t v = src[i >> 1]; - v = tosigned16(v); - word_buffer[i] = mult16signed(copy16lsb(v), lo_level, hi_level); - word_buffer[i + 1] = mult16signed(copy16msb(v), lo_level, hi_level); + uint32_t word = src[i >> 1]; + word = tosigned16(word); + uint32_t word_lsb = copy16lsb(word); + assignmul(word_lsb, &last_word, &active_lo_level, &active_hi_level, pending_lo_level, pending_hi_level); + word_buffer[i] = mult16signed(word_lsb, active_lo_level, active_hi_level); + word = copy16msb(word); + assignmul(word, &last_word, &active_lo_level, &active_hi_level, pending_lo_level, pending_hi_level); + word_buffer[i + 1] = mult16signed(word, active_lo_level, active_hi_level); } } } @@ -286,7 +327,8 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, if (MP_LIKELY(!self->base.samples_signed)) { word = tosigned16(word); } - word = mult16signed(word, lo_level, hi_level); + assignmul(word, &last_word, &active_lo_level, &active_hi_level, pending_lo_level, pending_hi_level); + word = mult16signed(word, active_lo_level, active_hi_level); hword_buffer[i] = pack8(word); } } else { @@ -295,8 +337,12 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, if (MP_LIKELY(!self->base.samples_signed)) { word = tosigned16(word); } - hword_buffer[i] = pack8(mult16signed(copy16lsb(word), lo_level, hi_level)); - hword_buffer[i + 1] = pack8(mult16signed(copy16msb(word), lo_level, hi_level)); + uint32_t word_lsb = copy16lsb(word); + assignmul(word_lsb, &last_word, &active_lo_level, &active_hi_level, pending_lo_level, pending_hi_level); + hword_buffer[i] = pack8(mult16signed(word_lsb, active_lo_level, active_hi_level)); + word = copy16msb(word); + assignmul(word, &last_word, &active_lo_level, &active_hi_level, pending_lo_level, pending_hi_level); + hword_buffer[i + 1] = pack8(mult16signed(word, active_lo_level, active_hi_level)); } } } @@ -306,13 +352,18 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { for (uint32_t i = 0; i < n; i++) { uint32_t word = src[i]; - word_buffer[i] = add16signed(mult16signed(word, lo_level, hi_level), word_buffer[i]); + assignmul(word, &last_word, &active_lo_level, &active_hi_level, pending_lo_level, pending_hi_level); + word_buffer[i] = add16signed(mult16signed(word, active_lo_level, active_hi_level), word_buffer[i]); } } else { for (uint32_t i = 0; i + 1 < n; i += 2) { uint32_t word = src[i >> 1]; - word_buffer[i] = add16signed(mult16signed(copy16lsb(word), lo_level, hi_level), word_buffer[i]); - word_buffer[i + 1] = add16signed(mult16signed(copy16msb(word), lo_level, hi_level), word_buffer[i + 1]); + uint32_t word_lsb = copy16lsb(word); + assignmul(word_lsb, &last_word, &active_lo_level, &active_hi_level, pending_lo_level, pending_hi_level); + word_buffer[i] = add16signed(mult16signed(word_lsb, active_lo_level, active_hi_level), word_buffer[i]); + word = copy16msb(word); + assignmul(word, &last_word, &active_lo_level, &active_hi_level, pending_lo_level, pending_hi_level); + word_buffer[i + 1] = add16signed(mult16signed(word, active_lo_level, active_hi_level), word_buffer[i + 1]); } } } else { @@ -320,14 +371,19 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, for (uint32_t i = 0; i < n; i++) { uint32_t word = src[i]; word = tosigned16(word); - word_buffer[i] = add16signed(mult16signed(word, lo_level, hi_level), word_buffer[i]); + assignmul(word, &last_word, &active_lo_level, &active_hi_level, pending_lo_level, pending_hi_level); + word_buffer[i] = add16signed(mult16signed(word, active_lo_level, active_hi_level), word_buffer[i]); } } else { for (uint32_t i = 0; i + 1 < n; i += 2) { uint32_t word = src[i >> 1]; word = tosigned16(word); - word_buffer[i] = add16signed(mult16signed(copy16lsb(word), lo_level, hi_level), word_buffer[i]); - word_buffer[i + 1] = add16signed(mult16signed(copy16msb(word), lo_level, hi_level), word_buffer[i + 1]); + uint32_t word_lsb = copy16lsb(word); + assignmul(word_lsb, &last_word, &active_lo_level, &active_hi_level, pending_lo_level, pending_hi_level); + word_buffer[i] = add16signed(mult16signed(word_lsb, active_lo_level, active_hi_level), word_buffer[i]); + word = copy16msb(word); + assignmul(word, &last_word, &active_lo_level, &active_hi_level, pending_lo_level, pending_hi_level); + word_buffer[i + 1] = add16signed(mult16signed(word, active_lo_level, active_hi_level), word_buffer[i + 1]); } } } @@ -340,7 +396,8 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, if (MP_LIKELY(!self->base.samples_signed)) { word = tosigned16(word); } - word = mult16signed(word, lo_level, hi_level); + assignmul(word, &last_word, &active_lo_level, &active_hi_level, pending_lo_level, pending_hi_level); + word = mult16signed(word, active_lo_level, active_hi_level); word = add16signed(word, unpack8(hword_buffer[i])); hword_buffer[i] = pack8(word); } @@ -350,8 +407,12 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, if (MP_LIKELY(!self->base.samples_signed)) { word = tosigned16(word); } - hword_buffer[i] = pack8(add16signed(mult16signed(copy16lsb(word), lo_level, hi_level), unpack8(hword_buffer[i]))); - hword_buffer[i + 1] = pack8(add16signed(mult16signed(copy16msb(word), lo_level, hi_level), unpack8(hword_buffer[i + 1]))); + uint32_t word_lsb = copy16lsb(word); + assignmul(word_lsb, &last_word, &active_lo_level, &active_hi_level, pending_lo_level, pending_hi_level); + hword_buffer[i] = pack8(add16signed(mult16signed(word_lsb, active_lo_level, active_hi_level), unpack8(hword_buffer[i]))); + word = copy16msb(word); + assignmul(word, &last_word, &active_lo_level, &active_hi_level, pending_lo_level, pending_hi_level); + hword_buffer[i + 1] = pack8(add16signed(mult16signed(word, active_lo_level, active_hi_level), unpack8(hword_buffer[i + 1]))); } } } @@ -365,6 +426,12 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, voice->remaining_buffer += n >> 1; voice->buffer_length -= n >> 1; } + + // Force the active level to match the pending level in the case that the conditions of a + // zero crossing weren't met within the last `SYNTHIO_MAX_DUR` frames. Will ensure minimal + // delay in level or panning changes at the potential expensive of an audible pop. + voice->active_lo_level = pending_lo_level; + voice->active_hi_level = pending_hi_level; } if (length && !voices_active) { diff --git a/shared-module/audiomixer/MixerVoice.c b/shared-module/audiomixer/MixerVoice.c index 3a598516e7c..af015fc46f1 100644 --- a/shared-module/audiomixer/MixerVoice.c +++ b/shared-module/audiomixer/MixerVoice.c @@ -70,11 +70,17 @@ void common_hal_audiomixer_mixervoice_play(audiomixer_mixervoice_obj_t *self, mp self->sample = sample; self->loop = loop; - audiosample_reset_buffer(sample, false, 0); - audioio_get_buffer_result_t result = audiosample_get_buffer(sample, false, 0, (uint8_t **)&self->remaining_buffer, &self->buffer_length); - // Track length in terms of words. - self->buffer_length /= sizeof(uint32_t); - self->more_data = result == GET_BUFFER_MORE_DATA; + common_hal_audiomixer_mixervoice_reset_buffer(self); +} + +void common_hal_audiomixer_mixervoice_reset_buffer(audiomixer_mixervoice_obj_t *self) { + if (self->sample != NULL) { + audiosample_reset_buffer(self->sample, false, 0); + audioio_get_buffer_result_t result = audiosample_get_buffer(self->sample, false, 0, (uint8_t **)&self->remaining_buffer, &self->buffer_length); + // Track length in terms of words. + self->buffer_length /= sizeof(uint32_t); + self->more_data = result == GET_BUFFER_MORE_DATA; + } } bool common_hal_audiomixer_mixervoice_get_playing(audiomixer_mixervoice_obj_t *self) { diff --git a/shared-module/audiomixer/MixerVoice.h b/shared-module/audiomixer/MixerVoice.h index 75e7d47faa3..db5cac1ca27 100644 --- a/shared-module/audiomixer/MixerVoice.h +++ b/shared-module/audiomixer/MixerVoice.h @@ -28,4 +28,6 @@ typedef struct { uint16_t level; int16_t panning; #endif + int32_t active_lo_level; + int32_t active_hi_level; } audiomixer_mixervoice_obj_t; diff --git a/shared-module/audiomp3/MP3Decoder.c b/shared-module/audiomp3/MP3Decoder.c index dc22c0be245..6f0b5d4723f 100644 --- a/shared-module/audiomp3/MP3Decoder.c +++ b/shared-module/audiomp3/MP3Decoder.c @@ -120,8 +120,13 @@ static void stream_set_blocking(audiomp3_mp3file_obj_t *self, bool block_ok) { * Sets self->eof if any read of the file returns 0 bytes */ static bool mp3file_update_inbuf_always(audiomp3_mp3file_obj_t *self, bool block_ok) { + // Every return from this function must be preceded by background_callback_allow(); + background_callback_prevent(); + if (self->eof || INPUT_BUFFER_SPACE(self->inbuf) == 0) { - return INPUT_BUFFER_AVAILABLE(self->inbuf) > 0; + bool result = INPUT_BUFFER_AVAILABLE(self->inbuf) > 0; + background_callback_allow(); + return result; } stream_set_blocking(self, block_ok); @@ -135,7 +140,8 @@ static bool mp3file_update_inbuf_always(audiomp3_mp3file_obj_t *self, bool block self->inbuf.read_off = 0; } - for (size_t to_read; !self->eof && (to_read = INPUT_BUFFER_SPACE(self->inbuf)) > 0;) { + for (size_t to_read; !self->eof && INPUT_BUFFER_SPACE(self->inbuf) > 0 && + (to_read = (size_t)INPUT_BUFFER_SPACE(self->inbuf)) > 0;) { uint8_t *write_ptr = self->inbuf.buf + self->inbuf.write_off; ssize_t n_read = stream_read(self->stream, write_ptr, to_read); @@ -145,6 +151,7 @@ static bool mp3file_update_inbuf_always(audiomp3_mp3file_obj_t *self, bool block break; } self->eof = true; + background_callback_allow(); mp_raise_OSError(errcode); } @@ -160,7 +167,10 @@ static bool mp3file_update_inbuf_always(audiomp3_mp3file_obj_t *self, bool block } // Return true iff there are at least some useful bytes in the buffer - return INPUT_BUFFER_AVAILABLE(self->inbuf) > 0; + bool result = INPUT_BUFFER_AVAILABLE(self->inbuf) > 0; + + background_callback_allow(); + return result; } /** Update the inbuf from a background callback. diff --git a/shared-module/displayio/Group.c b/shared-module/displayio/Group.c index a2bf685d0f7..84ebffdab6d 100644 --- a/shared-module/displayio/Group.c +++ b/shared-module/displayio/Group.c @@ -435,7 +435,7 @@ void displayio_group_finish_refresh(displayio_group_t *self) { } } -displayio_area_t *displayio_group_get_refresh_areas(displayio_group_t *self, displayio_area_t *tail) { +static displayio_area_t *group_get_refresh_areas_impl(displayio_group_t *self, displayio_area_t *tail) { if (self->item_removed) { self->dirty_area.next = tail; tail = &self->dirty_area; @@ -462,10 +462,76 @@ displayio_area_t *displayio_group_get_refresh_areas(displayio_group_t *self, dis layer = mp_obj_cast_to_native_base( self->members->items[i], &displayio_group_type); if (layer != MP_OBJ_NULL) { - tail = displayio_group_get_refresh_areas(layer, tail); + tail = group_get_refresh_areas_impl(layer, tail); continue; } } return tail; } + +// Filter the dirty-rect list by containment: unlink every area that is fully contained +// in another area in the list (a changing text label is the common case - the group +// dirties the whole label region via item_removed plus one area per glyph, and the +// glyph areas all sit inside the label region). Dropping a covered area is lossless: +// the same pixels are refreshed by the survivor, in fewer transfers. +// +// The nodes are OWNED BY THE ITEMS (TileGrid reuses dirty_area as its tile-space dirty +// accumulator between frames, vectorio keeps its current_area, ...), so their +// coordinates must never be modified here. The ONLY mutation is the .next relink, +// which is safe because every provider unconditionally reassigns .next at link time on +// every get_refresh_areas call and never reads it back. +// +// Nodes at and past `tail` belong to the caller, so the sweep uses `tail` as its end +// sentinel and never looks past it (every in-tree caller passes tail == NULL). +// +// One pass suffices: coordinates never change and unlinking preserves the relative +// order of the remaining nodes, so every pair of surviving nodes is examined. Equal +// areas satisfy the containment test in both directions; testing b-inside-a FIRST +// keeps the earlier node and drops the later one - the two tests must stay an else-if +// chain or equal areas would drop BOTH nodes and lose the area entirely. +static displayio_area_t *filter_out_redundant_areas(displayio_area_t *head, + const displayio_area_t *tail) { + displayio_area_t *a_prev = NULL; + displayio_area_t *a = head; + while (a != tail) { + // The casts from the const .next field are safe: every node before `tail` is a + // mutable item-owned struct; only the field's declared type is pointer-to-const. + displayio_area_t *b_prev = a; + displayio_area_t *b = (displayio_area_t *)a->next; + bool a_dropped = false; + while (b != tail) { + if (b->x1 >= a->x1 && b->y1 >= a->y1 && b->x2 <= a->x2 && b->y2 <= a->y2) { + // b is contained in a (equal areas land here): unlink b. + b_prev->next = b->next; + b = (displayio_area_t *)b_prev->next; + } else if (a->x1 >= b->x1 && a->y1 >= b->y1 && a->x2 <= b->x2 && a->y2 <= b->y2) { + // a is strictly contained in the later b: unlink a and move on. + if (a_prev == NULL) { + head = (displayio_area_t *)a->next; + } else { + a_prev->next = a->next; + } + a_dropped = true; + break; + } else { + b_prev = b; + b = (displayio_area_t *)b->next; + } + } + if (a_dropped) { + a = (a_prev == NULL) ? head : (displayio_area_t *)a_prev->next; + } else { + a_prev = a; + a = (displayio_area_t *)a->next; + } + } + return head; +} + +// Public entry point: displays call this once on their root group, so the whole tree's +// list is filtered in one place and no display type needs its own copy of the logic. +displayio_area_t *displayio_group_get_refresh_areas(displayio_group_t *self, displayio_area_t *tail) { + displayio_area_t *areas = group_get_refresh_areas_impl(self, tail); + return filter_out_redundant_areas(areas, tail); +} diff --git a/shared-module/picogame/Bitmap.h b/shared-module/picogame/Bitmap.h new file mode 100644 index 00000000000..887d904b203 --- /dev/null +++ b/shared-module/picogame/Bitmap.h @@ -0,0 +1,35 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT +// +// picogame: 2D game engine for the PicoPad and similar boards. +// Bitmap = an image atlas of one or more equal-size frames, arbitrary width/height. + +#pragma once + +#include +#include +#include "py/obj.h" + +enum { + PICOGAME_FMT_RGB565 = 0, // 2 bytes/pixel, values in display wire order + PICOGAME_FMT_PAL8 = 1, // 1 byte/pixel index into palette (wire-order RGB565) +}; + +typedef struct { + mp_obj_base_t base; + mp_obj_t data_obj; // keep the source buffer alive + mp_obj_t palette_obj; // keep the palette buffer alive (MP_OBJ_NULL for RGB565) + const uint8_t *data; // pixel data + const uint16_t *palette; // wire-order RGB565 entries (PAL8), else NULL + uint16_t width, height; // size of a single frame + uint16_t stride; // atlas width in pixels (>= width * frames for a horizontal atlas) + uint16_t transparent; // transparent key: palette index (PAL8) or wire color (RGB565) + uint16_t pal_entries; // palette length in entries (PAL8; 0 for RGB565). Informational: the + // blitter does NOT clamp - indices must be < this (see PAL8 blit contract). + uint8_t format; // PICOGAME_FMT_* + uint8_t frames; + bool has_transparent; +} picogame_bitmap_obj_t; diff --git a/shared-module/picogame/Canvas.c b/shared-module/picogame/Canvas.c new file mode 100644 index 00000000000..5021dbc95f9 --- /dev/null +++ b/shared-module/picogame/Canvas.c @@ -0,0 +1,630 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#include + +#include "shared-module/picogame/Canvas.h" +#include "shared-module/picogame/Bitmap.h" +#include "shared-module/picogame/__init__.h" +#include "shared-module/fontio/BuiltinFont.h" +#include "shared-bindings/displayio/Bitmap.h" + +// Thin wrappers over the shared int32 accumulator (dx1,dy1,dx2,dy2 are contiguous int32 at the +// struct tail). See picogame_dirty_* in __init__.c. +void picogame_canvas_dirty_reset(picogame_canvas_obj_t *cv) { + picogame_dirty_reset(&cv->dx1); +} + +void picogame_canvas_dirty_union(picogame_canvas_obj_t *cv, int x1, int y1, int x2, int y2) { + picogame_dirty_union(&cv->dx1, x1, y1, x2, y2); +} + +bool picogame_canvas_take_dirty(picogame_canvas_obj_t *cv, int *x1, int *y1, int *x2, int *y2) { + return picogame_dirty_take(&cv->dx1, x1, y1, x2, y2); +} + +// Union a canvas-local rect (clamped to the surface) into the dirty rect (scene coords). +static void mark(picogame_canvas_obj_t *cv, int lx1, int ly1, int lx2, int ly2) { + if (lx1 < 0) { + lx1 = 0; + } + if (ly1 < 0) { + ly1 = 0; + } + if (lx2 > cv->w) { + lx2 = cv->w; + } + if (ly2 > cv->h) { + ly2 = cv->h; + } + if (lx1 >= lx2 || ly1 >= ly2) { + return; + } + picogame_dirty_union(&cv->dx1, cv->x + lx1, cv->y + ly1, cv->x + lx2, cv->y + ly2); +} + +// NOT inlined on purpose: the shape primitives call put() many times (circle = +// 8 calls/iteration). Inlining bloated them (circle was ~1.4 KB); a real call keeps +// them small. Shapes aren't the hot path (the sprite/tilemap blits don't use put). +static __attribute__((noinline)) void put(picogame_canvas_obj_t *cv, int x, int y, uint16_t c) { + if (x >= 0 && y >= 0 && x < cv->w && y < cv->h) { + cv->data[y * cv->w + x] = c; + } +} + +// Fill `n` RGB565 pixels at `p` with `color`, word-filling two pixels per store (half the writes of +// a 16-bit loop); memset for the common 0 case. Handles a leading odd (2-byte-but-not-4-byte) address +// so it stays safe on Cortex-M0+ (RP2040), which faults on an unaligned 32-bit access - a StripDraw +// view's rows into the render strip can start on an odd pixel. This is the per-frame path for +// view.clear / Sky / HUD-bar / Fade fills, so the word-fill is worth it. +static void fill565(uint16_t *p, int n, uint16_t color) { + if (n <= 0) { + return; + } + if (color == 0) { + memset(p, 0, (size_t)n * 2); + return; + } + if ((uintptr_t)p & 3) { // align to 4 bytes: one leading pixel + *p++ = color; + n--; + } + uint32_t w = (uint32_t)color | ((uint32_t)color << 16); + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wcast-align" + uint32_t *w32 = (uint32_t *)p; // now 4-byte aligned + #pragma GCC diagnostic pop + int nw = n >> 1; + for (int i = 0; i < nw; i++) { + w32[i] = w; + } + if (n & 1) { // trailing odd pixel + p[n - 1] = color; + } +} + +// Defined with the filled shapes below; forward-declared for picogame_canvas_road. +static void span565(picogame_canvas_obj_t *cv, int y, int xs, int xe, uint16_t color); + +void picogame_canvas_clear(picogame_canvas_obj_t *cv, uint16_t color) { + fill565(cv->data, cv->w * cv->h, color); + mark(cv, 0, 0, cv->w, cv->h); +} + +void picogame_canvas_pixel(picogame_canvas_obj_t *cv, int x, int y, uint16_t color) { + put(cv, x, y, color); + mark(cv, x, y, x + 1, y + 1); +} + +void picogame_canvas_fill_rect(picogame_canvas_obj_t *cv, int x, int y, int w, int h, uint16_t color) { + int x2 = x + w, y2 = y + h; + int cx1 = x < 0 ? 0 : x, cy1 = y < 0 ? 0 : y; + int cx2 = x2 > cv->w ? cv->w : x2, cy2 = y2 > cv->h ? cv->h : y2; + for (int yy = cy1; yy < cy2; yy++) { + fill565(cv->data + yy * cv->w + cx1, cx2 - cx1, color); + } + mark(cv, x, y, x2, y2); +} + +void picogame_canvas_blit(picogame_canvas_obj_t *cv, picogame_bitmap_obj_t *bm, + int x, int y, int frame, bool flip_x, bool flip_y) { + // Composite a bitmap FRAME into the canvas buffer (honours the bitmap's transparent key). + // Reuses the sprite blit path, targeting the canvas's own RGB565 surface instead of a strip. + picogame_blit_bitmap(cv->data, cv->w, cv->h, 0, 0, bm, x, y, frame, flip_x, flip_y, false, NULL); + mark(cv, x, y, x + bm->width, y + bm->height); +} + +// Number of low zero bits (log2 for a power of 2; 0 for non-pow2, caught by caller). +static int log2_pow2(unsigned v) { + int n = 0; + while ((v & 1) == 0 && v > 1) { + v >>= 1; + n++; + } + return n; +} + +// One racing-road strip, all rows in one call (the OutRun-genre "draw_road" scanline loop - profiled +// at ~20-25 ms of Python on picobike: ~4 fill_rect boundary crossings + an int(float) phase per row). +// ri0 = the road-table row of THIS surface's row 0 (vy - horizon_base); negative rows are sky. +// tab = int16[ntab][5]: {edge_w, dash_hw, wb05_q8, wb07_q8, flags(bit0 = dashes allowed)} - static per +// game. rl/rr = per-frame integer road edges (road_edges output). d05/d07 = the frame's scrolling +// stripe/dash phases in Q8; the row's band parity is ((d05+wb05)>>8)&1, matching the Python +// int(d05f + wb05f) & 1 (both non-negative). colors = uint16[6]: {sky, road_a, road_b, rumble_a, +// rumble_b, dash}. Grass underneath and the finish-line chequer stay the caller's job (one fill_rect +// per strip / a few rows near the lap line - no reason to carry them in C). +void picogame_canvas_road(picogame_canvas_obj_t *cv, int ri0, + const int16_t *tab, int ntab, const int16_t *rl, const int16_t *rr, + int32_t d05_q8, int32_t d07_q8, const uint16_t *colors) { + int w = cv->w; + for (int ly = 0; ly < cv->h; ly++) { + int ri = ri0 + ly; + if (ri < 0) { // above the horizon: sky + fill565(&cv->data[ly * w], w, colors[0]); + continue; + } + if (ri >= ntab) { + ri = ntab - 1; + } + const int16_t *t = tab + ri * 5; + int band = (int)(((d05_q8 + t[2]) >> 8) & 1); + uint16_t road = band ? colors[1] : colors[2]; + uint16_t rumble = band ? colors[3] : colors[4]; + int l = rl[ri], r = rr[ri]; + if (r <= l) { + continue; + } + span565(cv, ly, l, r - 1, road); // fill_rect(l, w=r-l) covers l..r-1 + int ew = t[0]; + span565(cv, ly, l, l + ew - 1, rumble); + span565(cv, ly, r - ew, r - 1, rumble); + if ((t[4] & 1) && (((d07_q8 + t[3]) >> 8) & 1)) { + int mid = (l + r) >> 1, dw = t[1]; + span565(cv, ly, mid - dw, mid + dw - 1, colors[5]); + } + } + mark(cv, 0, 0, w, cv->h); +} + +// Context + row walker for the mode7 loop (each row derives rowdist/steps from its own sy). +typedef struct { + picogame_canvas_obj_t *cv; + const uint8_t *data; + const uint16_t *pal; + int fmt, stride, shx, shy, mx, my, horizon, y_off; + int32_t z, rx0, ry0, rsx, rsy, cam_x, cam_y; + bool transp; + uint16_t key; +} mode7_ctx_t; + +static void mode7_rows(void *arg, int lo, int hi) { + mode7_ctx_t *c = arg; + picogame_canvas_obj_t *cv = c->cv; + int w = cv->w; + #if defined(PICOGAME_HAS_INTERP) + int ltw = 16 - c->shx; + int lth = 16 - c->shy; + bool use_interp = (c->fmt == PICOGAME_FMT_PAL8) && !c->transp && c->pal != NULL + && c->stride == c->mx + 1 + && ltw >= 1 && lth >= 1 && ltw + lth <= 16; // lane1 shift = shy-ltw must be >= 0 + #endif + for (int sy = lo; sy < hi; sy++) { + int denom = (sy + c->y_off) - c->horizon; + if (denom <= 0) { + continue; + } + // 32-bit throughout (no 64-bit mul helper on the M0+): rowdist*coeff stays + // within int32 for sane camera params - the Python helper keeps z and the + // ray deltas small; extreme values degrade to wrong pixels, never a crash. + int32_t rowdist = c->z / denom; + int32_t stepx = (rowdist * c->rsx) >> 16; + int32_t stepy = (rowdist * c->rsy) >> 16; + int32_t fx = c->cam_x + ((rowdist * c->rx0) >> 16); + int32_t fy = c->cam_y + ((rowdist * c->ry0) >> 16); + uint16_t *drow = cv->data + sy * w; + #if defined(PICOGAME_HAS_INTERP) + if (use_interp) { + picogame_mode7_row_interp(drow, w, c->data, c->pal, + (uint32_t)fx, (uint32_t)fy, stepx, stepy, c->shx, c->shy, ltw, lth); + continue; + } + #endif + for (int sx = 0; sx < w; sx++) { + int tx = (fx >> c->shx) & c->mx, ty = (fy >> c->shy) & c->my; + uint16_t val; + if (src_pixel_s(c->fmt, c->data, c->pal, c->transp, c->key, ty * c->stride + tx, &val)) { + drow[sx] = val; + } + fx += stepx; + fy += stepy; + } + } +} + +void picogame_canvas_mode7(picogame_canvas_obj_t *cv, picogame_bitmap_obj_t *tex, + int horizon, int y_off, int32_t z, int32_t rx0, int32_t ry0, int32_t rsx, int32_t rsy, + int32_t cam_x, int32_t cam_y) { + // Perspective ground plane (Mode-7 / floorcaster). For each screen row below + // `horizon`, distance = z / (row - horizon) (16.16); the texture-space coord + // of the left edge is cam + distance*ray0, stepping by distance*rayDelta per + // pixel; sample `tex` (power-of-2, so wrap = a mask, and world 1.0 = one tile + // via a shift, no multiply). Integer throughout - no FPU needed (RP2040). + if (tex == NULL) { + return; + } + int tw = tex->width, th = tex->height; + if ((tw & (tw - 1)) || (th & (th - 1))) { // require power-of-2 dims + return; + } + int shx = 16 - log2_pow2((unsigned)tw); // world(1.0) -> one full tile + int shy = 16 - log2_pow2((unsigned)th); + int mx = tw - 1, my = th - 1, stride = tex->stride; + int fmt = tex->format; + const uint8_t *data = tex->data; + const uint16_t *pal = tex->palette; + bool transp = tex->has_transparent; + uint16_t key = tex->transparent; + // sy is a row WITHIN this surface (a StripDraw view is a Canvas onto one strip); + // the absolute screen row is sy + y_off, so the horizon test uses that. y_off = 0 + // for a full-screen Canvas, = the strip's screen y for a StripDraw view (0-RAM floor). + int y0 = horizon - y_off + 1; + if (y0 < 0) { + y0 = 0; + } + mode7_ctx_t ctx = { + cv, data, pal, fmt, stride, shx, shy, mx, my, horizon, y_off, + z, rx0, ry0, rsx, rsy, cam_x, cam_y, transp, key + }; + mode7_rows(&ctx, y0, cv->h); + mark(cv, 0, y0, cv->w, cv->h); +} + +void picogame_canvas_rect(picogame_canvas_obj_t *cv, int x, int y, int w, int h, uint16_t color) { + picogame_canvas_fill_rect(cv, x, y, w, 1, color); + picogame_canvas_fill_rect(cv, x, y + h - 1, w, 1, color); + picogame_canvas_fill_rect(cv, x, y, 1, h, color); + picogame_canvas_fill_rect(cv, x + w - 1, y, 1, h, color); +} + +void picogame_canvas_line(picogame_canvas_obj_t *cv, int x0, int y0, int x1, int y1, uint16_t color) { + int dx = x1 - x0, dy = y1 - y0; + int adx = dx < 0 ? -dx : dx; + int ady = dy < 0 ? -dy : dy; + int sx = dx < 0 ? -1 : 1; + int sy = dy < 0 ? -1 : 1; + int err = adx - ady; + int x = x0, y = y0; + while (true) { + put(cv, x, y, color); + if (x == x1 && y == y1) { + break; + } + int e2 = 2 * err; + if (e2 > -ady) { + err -= ady; + x += sx; + } + if (e2 < adx) { + err += adx; + y += sy; + } + } + int lx1 = x0 < x1 ? x0 : x1, ly1 = y0 < y1 ? y0 : y1; + int lx2 = (x0 > x1 ? x0 : x1) + 1, ly2 = (y0 > y1 ? y0 : y1) + 1; + mark(cv, lx1, ly1, lx2, ly2); +} + +// Clamp a row span to the surface and word-fill it (the span-pass idiom shared by the filled +// shapes; the per-pixel put() loops it replaced clipped and indexed every pixel). +static inline int64_t edge_slope(int32_t dx, int32_t dy) { + if (dx >= -32768 && dx <= 32767) { + return (int32_t)(dx << 16) / dy; + } + return ((int64_t)dx << 16) / dy; +} + +static void span565(picogame_canvas_obj_t *cv, int y, int xs, int xe, uint16_t color) { + if (y < 0 || y >= cv->h) { + return; + } + if (xs < 0) { + xs = 0; + } + if (xe >= cv->w) { + xe = cv->w - 1; + } + if (xs <= xe) { + fill565(&cv->data[y * cv->w + xs], xe - xs + 1, color); + } +} + +void picogame_canvas_fill_circle(picogame_canvas_obj_t *cv, int cx, int cy, int r, uint16_t color) { + // A filled circle IS fill_ellipse(r, r) - the ellipse row condition s^2*ry2 <= rr - dy^2*rx2 + // collapses to s^2 <= r^2 - dy^2 (host-proven byte-exact over 36k cases). Same delegation the + // OUTLINE circle already does; only r == 0 needs care (the ellipse rejects rx <= 0, a zero-radius + // circle is one pixel). Flash: this replaced a ~320 B twin of the ellipse body. + if (r < 0) { + return; + } + if (r == 0) { + picogame_canvas_pixel(cv, cx, cy, color); + return; + } + picogame_canvas_fill_ellipse(cv, cx, cy, r, r, color); +} + +void picogame_canvas_circle(picogame_canvas_obj_t *cv, int cx, int cy, int r, uint16_t color) { + picogame_canvas_ellipse(cv, cx, cy, r, r, color); // a circle is an ellipse with rx == ry +} + +void picogame_canvas_ring(picogame_canvas_obj_t *cv, int cx, int cy, int r, int thick, uint16_t color) { + if (r < 0) { + return; + } + int inner = r - thick; + if (inner < 0) { + inner = 0; + } + // Mirrored rows + decremental outer/inner widths, two word-filled spans per row - see fill_circle. + int out = r, ins = inner; + for (int dy = 0; dy <= r; dy++) { + long rr = (long)r * r - (long)dy * dy; // long (like ellipse): int r*r overflows at big radii + while ((long)out * out > rr) { + out--; + } + int two_seg = dy <= inner; + if (two_seg) { + int ri = inner * inner - dy * dy; + while (ins * ins > ri) { + ins--; + } + } + for (int half = 0; half < (dy ? 2 : 1); half++) { + int y = half ? cy - dy : cy + dy; + if (two_seg) { + span565(cv, y, cx - out, cx - ins - 1, color); + span565(cv, y, cx + ins + 1, cx + out, color); + } else { + span565(cv, y, cx - out, cx + out, color); + } + } + } + mark(cv, cx - r, cy - r, cx + r + 1, cy + r + 1); +} + +void picogame_canvas_triangle(picogame_canvas_obj_t *cv, + int x0, int y0, int x1, int y1, int x2, int y2, uint16_t color) { + picogame_canvas_line(cv, x0, y0, x1, y1, color); + picogame_canvas_line(cv, x1, y1, x2, y2, color); + picogame_canvas_line(cv, x2, y2, x0, y0, color); +} + +void picogame_canvas_fill_triangle(picogame_canvas_obj_t *cv, + int x0, int y0, int x1, int y1, int x2, int y2, uint16_t color) { + int X[3] = { x0, x1, x2 }, Y[3] = { y0, y1, y2 }; + for (int i = 0; i < 2; i++) { + for (int j = i + 1; j < 3; j++) { + if (Y[j] < Y[i]) { + int t = Y[i]; + Y[i] = Y[j]; + Y[j] = t; + t = X[i]; + X[i] = X[j]; + X[j] = t; + } + } + } + // Scanline fill via 16.16 edge DDA + word-filled spans. One 64-bit divide per EDGE replaces two + // 32-bit divides per ROW (M0+ has no HW divide, ~75 cyc each; a 20-row wall paid ~40 divides), + // and each row goes through fill565 (word stores) instead of a per-pixel clipped put(). Rows and + // spans clamp to the canvas up front, so a mostly off-screen triangle costs only its visible rows + // (the old loop walked EVERY row of huge triangles and clipped per pixel - quadratic blowup). + // Edge x differs from the old divide by at most 1 px (trunc vs floor; host-verified over 100k + // triangles), and convex quads - the box faces the 3D demos draw - stay seam-hole-free. + int w = cv->w, h = cv->h; + if (Y[0] < h && Y[2] >= 0) { + int64_t sAC = (Y[2] != Y[0]) ? edge_slope(X[2] - X[0], Y[2] - Y[0]) : 0; + int64_t sAB = (Y[1] != Y[0]) ? edge_slope(X[1] - X[0], Y[1] - Y[0]) : 0; + int64_t sBC = (Y[2] != Y[1]) ? edge_slope(X[2] - X[1], Y[2] - Y[1]) : 0; + uint16_t *data = cv->data; + // top half: rows [Y0, Y1) walk edges A->C and A->B + int ys = Y[0] < 0 ? 0 : Y[0]; + int ye = (Y[1] - 1) < (h - 1) ? (Y[1] - 1) : (h - 1); + int64_t accAC = ((int64_t)X[0] << 16) + sAC * (ys - Y[0]); + int64_t acc2 = ((int64_t)X[0] << 16) + sAB * (ys - Y[0]); + for (int y = ys; y <= ye; y++) { + int xac = (int)(accAC >> 16); + int xsh = (int)(acc2 >> 16); + int xs = xac < xsh ? xac : xsh, xe = xac < xsh ? xsh : xac; + if (xs < 0) { + xs = 0; + } + if (xe >= w) { + xe = w - 1; + } + if (xs <= xe) { + fill565(&data[y * w + xs], xe - xs + 1, color); + } + accAC += sAC; + acc2 += sAB; + } + // bottom half: rows [Y1, Y2] walk edges A->C and B->C (a flat bottom degenerates to sBC=0) + ys = Y[1] < 0 ? 0 : Y[1]; + ye = Y[2] < (h - 1) ? Y[2] : (h - 1); + accAC = ((int64_t)X[0] << 16) + sAC * (ys - Y[0]); + acc2 = ((int64_t)X[1] << 16) + sBC * (ys - Y[1]); + for (int y = ys; y <= ye; y++) { + int xac = (int)(accAC >> 16); + int xsh = (int)(acc2 >> 16); + int xs = xac < xsh ? xac : xsh, xe = xac < xsh ? xsh : xac; + if (xs < 0) { + xs = 0; + } + if (xe >= w) { + xe = w - 1; + } + if (xs <= xe) { + fill565(&data[y * w + xs], xe - xs + 1, color); + } + accAC += sAC; + acc2 += sBC; + } + } + int mnx = X[0] < X[1] ? X[0] : X[1]; + mnx = mnx < X[2] ? mnx : X[2]; + int mxx = X[0] > X[1] ? X[0] : X[1]; + mxx = mxx > X[2] ? mxx : X[2]; + mark(cv, mnx, Y[0], mxx + 1, Y[2] + 1); +} + +void picogame_canvas_ellipse(picogame_canvas_obj_t *cv, int cx, int cy, int rx, int ry, uint16_t color) { + if (rx <= 0 || ry <= 0) { + return; + } + // 32-bit `long`: rx2*ry2 stays in range while rx*ry <= 46340 (both radii <~210 px). That covers any + // canvas that fits in RAM on this target. A larger ellipse (only reachable on a big-RAM board with an + // oversized canvas) renders a wrong shape - never a fault, since put() clips every pixel to the canvas. + long rx2 = (long)rx * rx, ry2 = (long)ry * ry, rr = rx2 * ry2; + for (int dy = -ry; dy <= ry; dy++) { + int s = 0; + while ((long)(s + 1) * (s + 1) * ry2 + (long)dy * dy * rx2 <= rr) { + s++; + } + put(cv, cx - s, cy + dy, color); + put(cv, cx + s, cy + dy, color); + } + for (int dx = -rx; dx <= rx; dx++) { + int s = 0; + while ((long)(s + 1) * (s + 1) * rx2 + (long)dx * dx * ry2 <= rr) { + s++; + } + put(cv, cx + dx, cy - s, color); + put(cv, cx + dx, cy + s, color); + } + mark(cv, cx - rx, cy - ry, cx + rx + 1, cy + ry + 1); +} + +void picogame_canvas_fill_ellipse(picogame_canvas_obj_t *cv, int cx, int cy, int rx, int ry, uint16_t color) { + if (rx <= 0 || ry <= 0) { + return; + } + // 32-bit `long`: rx2*ry2 stays in range while rx*ry <= 46340 (both radii <~210 px). That covers any + // canvas that fits in RAM on this target. A larger ellipse (only reachable on a big-RAM board with an + // oversized canvas) renders a wrong shape - never a fault, since put() clips every pixel to the canvas. + long rx2 = (long)rx * rx, ry2 = (long)ry * ry, rr = rx2 * ry2; + // Mirrored rows + decremental width, spans word-filled - see fill_circle. + int s = rx; + for (int dy = 0; dy <= ry; dy++) { + long lim = rr - (long)dy * dy * rx2; // s*s*ry2 <= lim <=> the old (s+1)-increment bound + while ((long)s * s * ry2 > lim) { + s--; + } + span565(cv, cy + dy, cx - s, cx + s, color); + if (dy) { + span565(cv, cy - dy, cx - s, cx + s, color); + } + } + mark(cv, cx - rx, cy - ry, cx + rx + 1, cy + ry + 1); +} + +void picogame_canvas_fill_round_rect(picogame_canvas_obj_t *cv, int x, int y, int w, int h, int r, uint16_t color) { + if (r > w / 2) { + r = w / 2; + } + if (r > h / 2) { + r = h / 2; + } + if (r < 0) { + r = 0; + } + picogame_canvas_fill_rect(cv, x + r, y, w - 2 * r, h, color); + picogame_canvas_fill_rect(cv, x, y + r, r, h - 2 * r, color); + picogame_canvas_fill_rect(cv, x + w - r, y + r, r, h - 2 * r, color); + picogame_canvas_fill_circle(cv, x + r, y + r, r, color); + picogame_canvas_fill_circle(cv, x + w - r - 1, y + r, r, color); + picogame_canvas_fill_circle(cv, x + r, y + h - r - 1, r, color); + picogame_canvas_fill_circle(cv, x + w - r - 1, y + h - r - 1, r, color); +} + +void picogame_canvas_frame3d(picogame_canvas_obj_t *cv, int x, int y, int w, int h, uint16_t light, uint16_t dark) { + picogame_canvas_fill_rect(cv, x, y, w, 1, light); + picogame_canvas_fill_rect(cv, x, y, 1, h, light); + picogame_canvas_fill_rect(cv, x, y + h - 1, w, 1, dark); + picogame_canvas_fill_rect(cv, x + w - 1, y, 1, h, dark); +} + +void picogame_blit_canvas( + uint16_t *buf, int region_w, int strip_top, int strip_h, int x0, + picogame_canvas_obj_t *cv, int ox, int oy) { + // Reuse the bitmap blitter by viewing the canvas as a 1-frame RGB565 bitmap. + picogame_bitmap_obj_t bm; + bm.data = (const uint8_t *)cv->data; + bm.palette = NULL; + bm.width = cv->w; + bm.height = cv->h; + bm.stride = cv->w; + bm.transparent = cv->transparent; + bm.format = PICOGAME_FMT_RGB565; + bm.frames = 1; + bm.has_transparent = cv->has_transparent; + picogame_blit_bitmap(buf, region_w, strip_h, x0, strip_top, &bm, + cv->x + ox, cv->y + oy, 0, false, false, false, NULL); +} + +// Composite a string's glyphs straight into the surface in C: rasterize each glyph from the +// font's 1-bit atlas on the fly (no Python glyph cache, no per-call Bitmap/Sprite). Because the +// StripDraw `view` is a Canvas pointing at the live strip buffer, view.text() draws immediate-mode +// text into the frame with zero retained RAM - the same primitive serves retained Canvas screens. +void picogame_canvas_text(picogame_canvas_obj_t *cv, int x, int y, const char *text, + uint16_t fg, uint16_t bg, bool has_bg, const void *font) { + const fontio_builtinfont_t *f = font; + displayio_bitmap_t *sheet = (displayio_bitmap_t *)f->bitmap; + int fw = f->width, fh = f->height; + int tpr = sheet->width / fw; // glyph tiles per atlas row + int x0 = x; + // Hoist the canvas target + read the 1-bpp glyph atlas DIRECTLY (no per-pixel get_pixel/put calls). + // Clip each glyph rect to the canvas ONCE, then the inner loop is atlas-bit -> direct store. This is + // the per-frame path for StripDraw HUD text (repainted every frame), so it's worth the directness. + uint16_t *cdata = cv->data; + int cw = cv->w, ch = cv->h; + bool onebit = (sheet->bits_per_value == 1); // terminalio.FONT is 1-bpp; other fonts take the fallback + const uint8_t *sdata = (const uint8_t *)sheet->data; + int sstride_b = sheet->stride * 4; // atlas row stride in BYTES (stride counts uint32) + for (const uint8_t *p = (const uint8_t *)text; *p; p++) { + uint8_t gi = fontio_builtinfont_get_glyph_index(f, *p); + if (gi != 0xff) { // 0xff = no glyph -> blank advance + int tx = (gi % tpr) * fw, ty = (gi / tpr) * fh; + int gx0 = (x < 0) ? -x : 0; // clip the glyph rect to the canvas once + int gx1 = (x + fw > cw) ? cw - x : fw; + int gy0 = (y < 0) ? -y : 0; + int gy1 = (y + fh > ch) ? ch - y : fh; + for (int gy = gy0; gy < gy1; gy++) { + uint16_t *drow = cdata + (y + gy) * cw + x; // dst; index by gx (x+gx is in-bounds) + int sy = ty + gy; + if (onebit) { + const uint8_t *srow = sdata + (size_t)sy * sstride_b; + for (int gx = gx0; gx < gx1; gx++) { + int sx = tx + gx; + if ((srow[sx >> sheet->x_shift] >> (sheet->x_mask - (sx & sheet->x_mask))) & sheet->bitmask) { + drow[gx] = fg; + } else if (has_bg) { + drow[gx] = bg; + } + } + } else { + for (int gx = gx0; gx < gx1; gx++) { + if (common_hal_displayio_bitmap_get_pixel(sheet, tx + gx, sy)) { + drow[gx] = fg; + } else if (has_bg) { + drow[gx] = bg; + } + } + } + } + } + x += fw; + } + mark(cv, x0, y, x, y + fh); +} + +// Fill a screen-space triangle batch with per-triangle band reject - shared by the +// Canvas.fill_triangles binding and the compositor's Triangles layer (one loop, one place). +void picogame_fill_triangle_batch(picogame_canvas_obj_t *cv, const int16_t *v, + const uint16_t *col, int n, int xo, int yo) { + int cw = cv->w, ch = cv->h; + for (int i = 0; i < n; i++) { + const int16_t *p = v + i * 6; + int y0 = p[1] + yo, y1 = p[3] + yo, y2 = p[5] + yo; + if ((y0 < 0 && y1 < 0 && y2 < 0) || (y0 >= ch && y1 >= ch && y2 >= ch)) { + continue; + } + int x0 = p[0] + xo, x1 = p[2] + xo, x2 = p[4] + xo; + if ((x0 < 0 && x1 < 0 && x2 < 0) || (x0 >= cw && x1 >= cw && x2 >= cw)) { + continue; + } + picogame_canvas_fill_triangle(cv, x0, y0, x1, y1, x2, y2, col[i]); + } +} diff --git a/shared-module/picogame/Canvas.h b/shared-module/picogame/Canvas.h new file mode 100644 index 00000000000..45c73e43326 --- /dev/null +++ b/shared-module/picogame/Canvas.h @@ -0,0 +1,69 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT +// +// picogame Canvas: a RAM RGB565 surface (any size) you draw primitives into, +// composited as a Scene layer. The general home for shapes (fill_rect, line, +// circle, pixel) - accumulates a dirty rect (scene coords) so only redrawn +// areas repaint. Colors are wire-order (picogame.rgb565). + +#pragma once + +#include +#include +#include "py/obj.h" +#include "shared-module/picogame/Bitmap.h" + +typedef struct { + mp_obj_base_t base; + uint16_t *data; // w*h wire-order RGB565 + mp_obj_t data_obj; // backing buffer kept alive (MP_OBJ_NULL if m_new'd) + uint16_t w, h; + int32_t x, y; // scene position (int32: a canvas can sit past +-32767 px in a big world) + uint16_t transparent; + bool has_transparent; + int32_t dx1, dy1, dx2, dy2; // accumulated dirty rect (scene coords; int32, see x/y) +} picogame_canvas_obj_t; + +void picogame_canvas_dirty_reset(picogame_canvas_obj_t *cv); +// Grow the dirty rect to also cover a scene-coord rect (no surface clamping). +void picogame_canvas_dirty_union(picogame_canvas_obj_t *cv, int x1, int y1, int x2, int y2); +bool picogame_canvas_take_dirty(picogame_canvas_obj_t *cv, int *x1, int *y1, int *x2, int *y2); + +void picogame_canvas_clear(picogame_canvas_obj_t *cv, uint16_t color); +void picogame_canvas_pixel(picogame_canvas_obj_t *cv, int x, int y, uint16_t color); +void picogame_canvas_fill_rect(picogame_canvas_obj_t *cv, int x, int y, int w, int h, uint16_t color); +void picogame_canvas_blit(picogame_canvas_obj_t *cv, picogame_bitmap_obj_t *bm, int x, int y, int frame, bool flip_x, bool flip_y); +void picogame_canvas_rect(picogame_canvas_obj_t *cv, int x, int y, int w, int h, uint16_t color); +void picogame_canvas_line(picogame_canvas_obj_t *cv, int x0, int y0, int x1, int y1, uint16_t color); +void picogame_canvas_fill_circle(picogame_canvas_obj_t *cv, int cx, int cy, int r, uint16_t color); +void picogame_canvas_circle(picogame_canvas_obj_t *cv, int cx, int cy, int r, uint16_t color); +void picogame_canvas_ring(picogame_canvas_obj_t *cv, int cx, int cy, int r, int thick, uint16_t color); +void picogame_canvas_triangle(picogame_canvas_obj_t *cv, int x0, int y0, int x1, int y1, int x2, int y2, uint16_t color); +void picogame_canvas_fill_triangle(picogame_canvas_obj_t *cv, int x0, int y0, int x1, int y1, int x2, int y2, uint16_t color); +void picogame_canvas_ellipse(picogame_canvas_obj_t *cv, int cx, int cy, int rx, int ry, uint16_t color); +void picogame_canvas_fill_ellipse(picogame_canvas_obj_t *cv, int cx, int cy, int rx, int ry, uint16_t color); +void picogame_canvas_fill_round_rect(picogame_canvas_obj_t *cv, int x, int y, int w, int h, int r, uint16_t color); +void picogame_canvas_frame3d(picogame_canvas_obj_t *cv, int x, int y, int w, int h, uint16_t light, uint16_t dark); +void picogame_canvas_text(picogame_canvas_obj_t *cv, int x, int y, const char *text, + uint16_t fg, uint16_t bg, bool has_bg, const void *font); +// Mode-7 perspective ground plane: fill rows below `horizon` with a receding view +// of `tex` (power-of-2 dims). Args are 16.16 fixed-point (a Python helper computes +// them from camera angle/pos/fov). See picogame_canvas_mode7 for the exact math. +// One racing-road strip: per-row spans (road/rumbles/dashes/sky) from precomputed tables. +void picogame_canvas_road(picogame_canvas_obj_t *cv, int ri0, + const int16_t *tab, int ntab, const int16_t *rl, const int16_t *rr, + int32_t d05_q8, int32_t d07_q8, const uint16_t *colors); + +void picogame_canvas_mode7(picogame_canvas_obj_t *cv, picogame_bitmap_obj_t *tex, + int horizon, int y_off, int32_t z, int32_t rx0, int32_t ry0, int32_t rsx, int32_t rsy, + int32_t cam_x, int32_t cam_y); + +void picogame_blit_canvas( + uint16_t *buf, int region_w, int strip_top, int strip_h, int x0, + picogame_canvas_obj_t *cv, int ox, int oy); + +void picogame_fill_triangle_batch(picogame_canvas_obj_t *cv, const int16_t *v, + const uint16_t *col, int n, int xo, int yo); diff --git a/shared-module/picogame/Particles.c b/shared-module/picogame/Particles.c new file mode 100644 index 00000000000..87e6917f479 --- /dev/null +++ b/shared-module/picogame/Particles.c @@ -0,0 +1,163 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#include "shared-module/picogame/Particles.h" +#include "shared-module/picogame/__init__.h" + +static uint32_t s_prng = 0x1234abcdu; + +static int32_t prng_range(int32_t lo, int32_t hi) { + s_prng ^= s_prng << 13; + s_prng ^= s_prng >> 17; + s_prng ^= s_prng << 5; + if (hi <= lo) { + return lo; + } + return lo + (int32_t)(s_prng % (uint32_t)(hi - lo + 1)); +} + +static void swap_remove(picogame_particles_obj_t *ps, int i) { + int last = ps->count - 1; + ps->px[i] = ps->px[last]; + ps->py[i] = ps->py[last]; + ps->vx[i] = ps->vx[last]; + ps->vy[i] = ps->vy[last]; + ps->life[i] = ps->life[last]; + ps->life0[i] = ps->life0[last]; + ps->color[i] = ps->color[last]; + ps->count--; +} + +// Dim a wire-order RGB565 color to num/den of its brightness (per channel). +static inline uint16_t scale_wire565(uint16_t wire, int num, int den) { + uint16_t c = (uint16_t)((wire >> 8) | (wire << 8)); // wire -> native + int r = ((c >> 11) & 0x1F) * num / den; + int g = ((c >> 5) & 0x3F) * num / den; + int b = (c & 0x1F) * num / den; + uint16_t out = (uint16_t)((r << 11) | (g << 5) | b); + return (uint16_t)((out >> 8) | (out << 8)); // native -> wire +} + +void picogame_particles_emit(picogame_particles_obj_t *ps, int x, int y, + int count, int speed, int life, uint16_t color) { + int sp = speed * 256; // px/tick -> 8.8 + for (int k = 0; k < count && ps->count < ps->cap; k++) { + int i = ps->count++; + ps->px[i] = x << 8; + ps->py[i] = y << 8; + ps->vx[i] = (int16_t)prng_range(-sp, sp); + ps->vy[i] = (int16_t)prng_range(-sp, sp); + ps->life[i] = life; + ps->life0[i] = (uint16_t)(life > 0 ? life : 1); + ps->color[i] = color; + } +} + +void picogame_particles_update(picogame_particles_obj_t *ps) { + int16_t g = ps->gravity; + int x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff - 1, y2 = -0x7fffffff - 1; // INT32: big-world + int sz = ps->size; + int i = 0; + while (i < ps->count) { + ps->px[i] += ps->vx[i]; + ps->py[i] += ps->vy[i]; + ps->vy[i] += g; + if (ps->life[i] == 0) { + swap_remove(ps, i); + continue; // re-process the swapped-in particle + } + ps->life[i]--; + int sx = ps->px[i] >> 8; + int sy = ps->py[i] >> 8; + if (sx < x1) { + x1 = sx; + } + if (sy < y1) { + y1 = sy; + } + if (sx + sz > x2) { + x2 = sx + sz; + } + if (sy + sz > y2) { + y2 = sy + sz; + } + i++; + } + // current-frame bbox becomes "previous" on the next take_dirty + ps->px1 = ps->cx1; + ps->py1 = ps->cy1; + ps->px2 = ps->cx2; + ps->py2 = ps->cy2; + if (ps->count > 0) { + ps->cx1 = x1; + ps->cy1 = y1; + ps->cx2 = x2; + ps->cy2 = y2; + } else { + picogame_dirty_reset(&ps->cx1); // empty -> INT32 sentinels (cx1,cy1,cx2,cy2 are contiguous) + } +} + +bool picogame_particles_take_dirty(picogame_particles_obj_t *ps, + int *x1, int *y1, int *x2, int *y2) { + int ax1 = ps->cx1 < ps->px1 ? ps->cx1 : ps->px1; + int ay1 = ps->cy1 < ps->py1 ? ps->cy1 : ps->py1; + int ax2 = ps->cx2 > ps->px2 ? ps->cx2 : ps->px2; + int ay2 = ps->cy2 > ps->py2 ? ps->cy2 : ps->py2; + // consume the previous box so a static system stops reporting dirty + ps->px1 = ps->cx1; + ps->py1 = ps->cy1; + ps->px2 = ps->cx2; + ps->py2 = ps->cy2; + if (ax1 >= ax2 || ay1 >= ay2) { + return false; + } + *x1 = ax1; + *y1 = ay1; + *x2 = ax2; + *y2 = ay2; + return true; +} + +void picogame_particles_clear(picogame_particles_obj_t *ps) { + // Move the currently-drawn region into "previous" and empty "current", so the next + // take_dirty reports the old pixels ONCE (erasing the cleared particles) then goes quiet. + ps->px1 = ps->cx1; + ps->py1 = ps->cy1; + ps->px2 = ps->cx2; + ps->py2 = ps->cy2; + picogame_dirty_reset(&ps->cx1); // empty -> INT32 sentinels (cx1,cy1,cx2,cy2 are contiguous) + ps->count = 0; +} + +void picogame_blit_particles( + uint16_t *buf, int region_w, int strip_top, int strip_h, int x0, + picogame_particles_obj_t *ps, int ox, int oy) { + int sz = ps->size; + int rx2 = x0 + region_w; + int ry2 = strip_top + strip_h; + for (int i = 0; i < ps->count; i++) { + int sx = (ps->px[i] >> 8) + ox; + int sy = (ps->py[i] >> 8) + oy; + int xs = picogame_imax(sx, x0); + int ys = picogame_imax(sy, strip_top); + int xe = picogame_imin(sx + sz, rx2); + int ye = picogame_imin(sy + sz, ry2); + if (xs >= xe || ys >= ye) { + continue; + } + uint16_t c = ps->color[i]; + if (ps->fade) { + c = scale_wire565(c, ps->life[i], ps->life0[i]); + } + for (int y = ys; y < ye; y++) { + uint16_t *dst = buf + (y - strip_top) * region_w + (xs - x0); + for (int x = xs; x < xe; x++) { + *dst++ = c; + } + } + } +} diff --git a/shared-module/picogame/Particles.h b/shared-module/picogame/Particles.h new file mode 100644 index 00000000000..faaf7a4989b --- /dev/null +++ b/shared-module/picogame/Particles.h @@ -0,0 +1,45 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT +// +// picogame particle system: a pooled set of small moving dots rendered as one +// Scene layer (individual sprites would be far too heavy). Positions/velocities +// are 24.8 / 8.8 fixed-point for sub-pixel motion. Tracks a dirty rect spanning +// the previous and current frames so moving particles leave no trails. + +#pragma once + +#include +#include +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + int32_t *px, *py; // position, 24.8 fixed-point, scene coords + int16_t *vx, *vy; // velocity, 8.8 fixed-point per tick + uint16_t *life; // ticks remaining + uint16_t *life0; // life at spawn (for the fade ramp) + uint16_t *color; // wire-order RGB565 + uint16_t cap, count; + int16_t gravity; // 8.8, added to vy each tick + uint8_t size; // particle size in pixels + bool fade; // dim each particle toward black as it ages + // dirty bounding boxes (scene coords): previous frame and current frame. + // int32 (not int16): positions are 24.8 in int32, so emitters past +-32767 px would truncate. + int32_t cx1, cy1, cx2, cy2; + int32_t px1, py1, px2, py2; +} picogame_particles_obj_t; + +void picogame_particles_emit(picogame_particles_obj_t *ps, int x, int y, + int count, int speed, int life, uint16_t color); +void picogame_particles_update(picogame_particles_obj_t *ps); +// Remove all particles, marking their last-drawn region dirty once so they get erased. +void picogame_particles_clear(picogame_particles_obj_t *ps); +// Returns true + the dirty rect (scene coords) spanning last+current frames. +bool picogame_particles_take_dirty(picogame_particles_obj_t *ps, + int *x1, int *y1, int *x2, int *y2); +void picogame_blit_particles( + uint16_t *buf, int region_w, int strip_top, int strip_h, int x0, + picogame_particles_obj_t *ps, int ox, int oy); diff --git a/shared-module/picogame/Scene.c b/shared-module/picogame/Scene.c new file mode 100644 index 00000000000..0d9e31c6fbc --- /dev/null +++ b/shared-module/picogame/Scene.c @@ -0,0 +1,226 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#include "shared-module/picogame/Scene.h" +#include "shared-module/picogame/__init__.h" +#include "shared-module/picogame/Bitmap.h" +#include "shared-module/picogame/Tilemap.h" +#include "shared-module/picogame/Particles.h" +#include "shared-module/picogame/Canvas.h" + +// Raw change rects collected before merging. Caps how many disjoint changes we +// track in one frame; on overflow we safely fall back to a full-screen repaint. +#define PICOGAME_RAW_RECTS 64 + +static inline bool rects_overlap(const picogame_rect_t *a, const picogame_rect_t *b) { + return a->x1 < b->x2 && b->x1 < a->x2 && a->y1 < b->y2 && b->y1 < a->y2; +} + +static inline void rect_merge(picogame_rect_t *a, const picogame_rect_t *b) { + if (b->x1 < a->x1) { + a->x1 = b->x1; + } + if (b->y1 < a->y1) { + a->y1 = b->y1; + } + if (b->x2 > a->x2) { + a->x2 = b->x2; + } + if (b->y2 > a->y2) { + a->y2 = b->y2; + } +} + +static inline long rect_area(const picogame_rect_t *r) { + return (long)(r->x2 - r->x1) * (long)(r->y2 - r->y1); +} + +// Compute up to `max_rects` mostly-disjoint dirty rectangles (screen coords) and +// update the per-sprite snapshots. Returns the rect count (0 = nothing changed). +// Collecting per-change rects (instead of one union) means scattered movers no +// longer inflate the repaint to the whole screen. +// Out-of-line rect append for compute_dirty_rects below: the old macro expanded ~30 B +// four times; a real (noinline) call keeps each site at argument setup only. +static __attribute__((noinline)) void add_rect(picogame_rect_t *raw, int *nr, bool *overflow, + int x1, int y1, int x2, int y2) { + if (*nr < PICOGAME_RAW_RECTS) { + raw[*nr].x1 = x1; + raw[*nr].y1 = y1; + raw[*nr].x2 = x2; + raw[*nr].y2 = y2; + (*nr)++; + } else { + *overflow = true; + } +} + +int picogame_scene_compute_dirty_rects( + mp_obj_t *items, uint8_t *kinds, picogame_snapshot_t *snap, size_t n, + int screen_w, int screen_h, int ox, int oy, + picogame_rect_t *out, int max_rects) { + + picogame_rect_t raw[PICOGAME_RAW_RECTS]; + int nr = 0; + bool overflow = false; + + // Rects are stored in SCREEN coords: non-fixed items get the view offset added + // here (per item), fixed (HUD) items don't - so no uniform offset at the end. + #define ADD_RECT(ax, ay, bx, by) \ + add_rect(raw, &nr, &overflow, (ax) + iox, (ay) + ioy, (bx) + iox, (by) + ioy) + + for (size_t i = 0; i < n; i++) { + uint8_t rawk = kinds[i]; + uint8_t kind = rawk & PICOGAME_KIND_MASK; + int iox = (rawk & PICOGAME_KIND_FIXED) ? 0 : ox; + int ioy = (rawk & PICOGAME_KIND_FIXED) ? 0 : oy; + if (kind != PICOGAME_KIND_SPRITE) { + int tx1, ty1, tx2, ty2; + bool d = false; + if (kind == PICOGAME_KIND_TILEMAP) { + d = picogame_tilemap_take_dirty(MP_OBJ_TO_PTR(items[i]), &tx1, &ty1, &tx2, &ty2); + } else if (kind == PICOGAME_KIND_PARTICLES) { + d = picogame_particles_take_dirty(MP_OBJ_TO_PTR(items[i]), &tx1, &ty1, &tx2, &ty2); + } else if (kind == PICOGAME_KIND_CANVAS) { + d = picogame_canvas_take_dirty(MP_OBJ_TO_PTR(items[i]), &tx1, &ty1, &tx2, &ty2); + } else if (kind == PICOGAME_KIND_STRIPDRAW) { + // No retained pixels to diff. always_dirty -> repaint the whole rect every frame + // (animated content). Otherwise repaint only the accumulated invalidate() rect - the + // same take_dirty contract as Canvas/Tilemap, so on-change UI repaints just its region + // (and still re-runs when another layer's dirty rect overlaps it). + picogame_stripdraw_obj_t *sd = MP_OBJ_TO_PTR(items[i]); + if (sd->always_dirty) { + tx1 = sd->x; + ty1 = sd->y; + tx2 = sd->x + sd->w; + ty2 = sd->y + sd->h; + d = true; + } else { + d = picogame_dirty_take(&sd->dx1, &tx1, &ty1, &tx2, &ty2); + } + } else if (kind == PICOGAME_KIND_TRIANGLES) { + // Screen-space batch: count-set marked a full-screen dirty (clipped later). + picogame_triangles_obj_t *t = MP_OBJ_TO_PTR(items[i]); + d = picogame_dirty_take(&t->dx1, &tx1, &ty1, &tx2, &ty2); + } + if (d) { + ADD_RECT(tx1, ty1, tx2, ty2); + } + continue; + } + + picogame_sprite_obj_t *s = MP_OBJ_TO_PTR(items[i]); + picogame_snapshot_t *sn = &snap[i]; + + // Snapshot tracks the drawn screen AABB (already includes scale + rotation), + // so position/anchor/size/scale/angle/bitmap changes are all detected. + int ax1, ay1, ax2, ay2; + picogame_sprite_aabb(s, &ax1, &ay1, &ax2, &ay2); + picogame_bitmap_obj_t *bm = s->bitmap; + bool changed = (ax1 != sn->x) || (ay1 != sn->y) || + ((ax2 - ax1) != sn->w) || ((ay2 - ay1) != sn->h) || + (s->frame != sn->frame) || (s->flags != sn->flags) || + (s->scale != sn->scale) || (s->angle != sn->angle) || + (s->seq != sn->seq) || // touch(): in-place bitmap content change + (s->dither != sn->dither) || // translucency level animation + (s->flash_color != sn->flash_color) || // flash/tint colour-only change (flag stays set) + ((void *)bm != sn->bitmap); + if (!changed) { + continue; + } + + // Old rect = previous AABB (snapshot); new rect = current AABB. + if (sn->flags & PICOGAME_SPR_VISIBLE) { + ADD_RECT(sn->x, sn->y, sn->x + sn->w, sn->y + sn->h); + } + if (s->flags & PICOGAME_SPR_VISIBLE) { + ADD_RECT(ax1, ay1, ax2, ay2); + } + + sn->x = ax1; + sn->y = ay1; + sn->w = ax2 - ax1; + sn->h = ay2 - ay1; + sn->bitmap = (void *)bm; + sn->frame = s->frame; + sn->flags = s->flags; + sn->scale = s->scale; + sn->angle = s->angle; + sn->seq = s->seq; + sn->dither = s->dither; + sn->flash_color = s->flash_color; + } +#undef ADD_RECT + + if (nr == 0) { + return 0; + } + // Too many changes to track individually -> one full-screen repaint is both + // correct and likely cheaper than dozens of windows. + if (overflow) { + out[0].x1 = 0; + out[0].y1 = 0; + out[0].x2 = screen_w; + out[0].y2 = screen_h; + return 1; + } + + // Merge overlapping rects (avoids painting the same pixels twice) until stable. + bool again = true; + while (again) { + again = false; + for (int i = 0; i < nr; i++) { + for (int j = i + 1; j < nr; j++) { + if (rects_overlap(&raw[i], &raw[j])) { + rect_merge(&raw[i], &raw[j]); + raw[j] = raw[nr - 1]; + nr--; + again = true; + j--; + } + } + } + } + + // Cap the count by repeatedly merging the pair that wastes the fewest pixels. + while (nr > max_rects) { + int bi = 0, bj = 1; + long best = -1; + for (int i = 0; i < nr; i++) { + long area_i = rect_area(&raw[i]); // loop-invariant in j -> hoist out + for (int j = i + 1; j < nr; j++) { + picogame_rect_t u = raw[i]; + rect_merge(&u, &raw[j]); + long waste = rect_area(&u) - area_i - rect_area(&raw[j]); + if (best < 0 || waste < best) { + best = waste; + bi = i; + bj = j; + } + } + } + rect_merge(&raw[bi], &raw[bj]); + raw[bj] = raw[nr - 1]; + nr--; + } + + // Rects are already in screen coords (offset applied per item). Clip + drop empties. + int outn = 0; + for (int i = 0; i < nr; i++) { + int x1 = picogame_imax(raw[i].x1, 0); + int y1 = picogame_imax(raw[i].y1, 0); + int x2 = picogame_imin(raw[i].x2, screen_w); + int y2 = picogame_imin(raw[i].y2, screen_h); + if (x1 >= x2 || y1 >= y2) { + continue; + } + out[outn].x1 = x1; + out[outn].y1 = y1; + out[outn].x2 = x2; + out[outn].y2 = y2; + outn++; + } + return outn; +} diff --git a/shared-module/picogame/Scene.h b/shared-module/picogame/Scene.h new file mode 100644 index 00000000000..bd1e83937e3 --- /dev/null +++ b/shared-module/picogame/Scene.h @@ -0,0 +1,80 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT +// +// picogame retained-mode scene with dirty-rectangle tracking. The scene owns a +// list of sprites and a snapshot of their state from the previous frame; each +// refresh diffs against the snapshot to compute the changed regions and repaints +// only those: up to PICOGAME_MAX_DIRTY_RECTS (6) mostly-disjoint rectangles, each +// rendered into its own clamped SPI window, so several separated moving objects +// stay cheap; beyond that they merge toward a fuller redraw. + +#pragma once + +#include +#include +#include +#include "py/obj.h" +#include "shared-module/picogame/Sprite.h" + +typedef struct { + int32_t x, y; + int32_t w, h; // drawn SCENE-space AABB (top-left + size) when last drawn - already accounts + // for scale + rotation, so it is the old-rect on any change. int32 (not int16): + // sprite coords are 24.8 in int32, so a big scrolling world exceeds +-32767 px + void *bitmap; // bitmap identity when last drawn (detect graphic swaps); never + // dereferenced - compared for inequality only, so it is safe + // even if the previous bitmap has since been freed + uint16_t scale; // draw scale (8.8) when last drawn - detect scale changes + int16_t angle; // rotation when last drawn - detect rotation changes + uint8_t frame; + uint8_t flags; + uint8_t seq; // sprite.seq when last drawn - detect touch() (in-place content change) + uint8_t dither; // dither level when last drawn - detect translucency animation + uint16_t flash_color; // flash/tint colour when last drawn - detect a colour-only change + // (the effect flag stays set, so without this red->blue wouldn't repaint) +} picogame_snapshot_t; + +typedef struct { + int x1, y1, x2, y2; // screen-space dirty rectangle [x1,x2) x [y1,y2) +} picogame_rect_t; + +typedef struct { + mp_obj_base_t base; + mp_obj_t display; // picogame.Display (transport; also kept alive) + mp_obj_t buf_a; // strip buffer A (kept alive) + mp_obj_t buf_b; // strip buffer B (kept alive) + mp_obj_t *items; // sprite / tilemap objects (GC-scanned -> stay alive) + uint8_t *kinds; // PICOGAME_KIND_* per item + picogame_snapshot_t *snap; // previous-frame state; snap[i] is unused when + // kinds[i] == TILEMAP (tilemaps track their own dirty rect) + uint16_t count; + uint16_t cap; + uint16_t background; + int32_t ox, oy; // view offset: screen position of scene origin (camera/centering); int32 so a + // large-world camera can scroll past +-32767 px without truncating the offset + int16_t top, bottom, left, right; // reserved insets (px): the scene renders only the + // play rect [left, w-right) x [top, h-bottom) and never touches the + // border around it - the app owns it (HUD bars, side panels, frame). + bool cleared; // false until the first full-screen paint (covers stale pixels) + bool fast; // true: display is a fast picogame.Display (DMA); false: a plain + // busdisplay rendered via the portable bus.send fallback + #if CIRCUITPY_PICOGAME_FRAMEBUFFER + bool fb_target; // true: self->display is a picogame.Framebuffer (RAM scanout buffer); + // refresh() composites dirty rects straight into it, no bus. Mutually + // exclusive with `fast` and the busdisplay path. + #endif + mp_obj_t dirty_rect; // reusable [x1,y1,x2,y2] list returned by refresh() (no + // per-frame tuple allocation / GC churn) +} picogame_scene_obj_t; + +// Diff items against snapshots and produce up to `max_rects` mostly-disjoint +// dirty rectangles (screen coords), updating snapshots / draining layer dirties. +// Returns the rect count (0 = nothing changed). Scattered movers yield several +// small rects instead of one screen-spanning union. +int picogame_scene_compute_dirty_rects( + mp_obj_t *items, uint8_t *kinds, picogame_snapshot_t *snap, size_t n, + int screen_w, int screen_h, int ox, int oy, + picogame_rect_t *out, int max_rects); diff --git a/shared-module/picogame/Sprite.h b/shared-module/picogame/Sprite.h new file mode 100644 index 00000000000..157b94e652b --- /dev/null +++ b/shared-module/picogame/Sprite.h @@ -0,0 +1,48 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT +// +// picogame: a positioned, animatable instance of a Bitmap. + +#pragma once + +#include +#include "py/obj.h" +#include "shared-module/picogame/Bitmap.h" + +enum { + PICOGAME_SPR_VISIBLE = 1 << 0, + PICOGAME_SPR_FLIP_X = 1 << 1, + PICOGAME_SPR_FLIP_Y = 1 << 2, + PICOGAME_SPR_SHADOW = 1 << 3, // draw opaque pixels as a darkened destination (shadow/dim) + PICOGAME_SPR_FLASH = 1 << 4, // draw opaque pixels as a solid colour (hit-flash) + PICOGAME_SPR_DITHER = 1 << 5, // skip pixels via a Bayer pattern -> fake transparency + PICOGAME_SPR_TINT = 1 << 6, // multiply opaque pixels by a colour (keeps shading) + PICOGAME_SPR_TRANSPOSE = 1 << 7, // swap x/y -> cheap 90deg (with flips = all 8 orientations) +}; + +typedef struct { + mp_obj_base_t base; + picogame_bitmap_obj_t *bitmap; + mp_obj_t data; // arbitrary user payload (game state); GC-scanned + int32_t x, y; // position, 24.8 fixed-point (1/256 px), scene coords + uint16_t anchor_x; // pivot as a 1/256 fraction of width: 0=left, 128~=center, 256=right + uint16_t anchor_y; // pivot as a 1/256 fraction of height: 0=top, 128~=center, 256=bottom + uint16_t scale; // uniform draw scale, 8.8 fixed-point (256 = 1.0x); nearest-neighbour + int16_t angle; // rotation about the anchor, whole degrees (0 = axis-aligned fast path) + uint16_t flash_color; // FLASH mode: wire-order RGB565 that replaces opaque pixels + uint8_t frame; + uint8_t flags; + uint8_t seq; // bumped by touch() to force a repaint after an in-place bitmap mutation + uint8_t dither; // DITHER mode: transparency level 0..16 (0=opaque, 16=invisible) + // ---- affine transform cache (angle != 0 path). Filled lazily on first use, invalidated + // by the scale/angle/bitmap/anchor setters (xf_valid = 0). POSITION-INDEPENDENT: the bbox + // is relative to the sprite's integer position, ic/is are the 16.16 inverse-map steps. + // Saves the trig LUT + 4-corner bbox + two software divides that otherwise re-run once per + // STRIP the sprite touches (~6x/frame at strip_h=8) plus once for the dirty-rect AABB. + uint8_t xf_valid; + int16_t xf_minx, xf_miny, xf_maxx, xf_maxy; // corners bbox relative to (x>>8, y>>8) + int32_t xf_ic, xf_is; // inverse-map steps (16.16) +} picogame_sprite_obj_t; diff --git a/shared-module/picogame/Tilemap.c b/shared-module/picogame/Tilemap.c new file mode 100644 index 00000000000..5be8efb1c37 --- /dev/null +++ b/shared-module/picogame/Tilemap.c @@ -0,0 +1,100 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#include "shared-module/picogame/Tilemap.h" +#include "shared-module/picogame/__init__.h" + +// Floor division for b > 0. +static inline int floordiv(int a, int b) { + return (a >= 0) ? (a / b) : -(((-a) + b - 1) / b); +} + +// log2 of a power-of-2 (small tile dims), without a __ctzsi2 lib call on the M0+ (no CLZ/RBIT). +static inline int pow2_shift(unsigned v) { + int n = 0; + while (v > 1u) { + v >>= 1; + n++; + } + return n; +} + +// Thin wrappers over the shared int32 accumulator (dx1,dy1,dx2,dy2 are contiguous int32 at the +// struct tail). See picogame_dirty_* in __init__.c. +void picogame_tilemap_dirty_reset(picogame_tilemap_obj_t *tm) { + picogame_dirty_reset(&tm->dx1); +} + +void picogame_tilemap_dirty_union(picogame_tilemap_obj_t *tm, int x1, int y1, int x2, int y2) { + picogame_dirty_union(&tm->dx1, x1, y1, x2, y2); +} + +bool picogame_tilemap_take_dirty(picogame_tilemap_obj_t *tm, int *x1, int *y1, int *x2, int *y2) { + return picogame_dirty_take(&tm->dx1, x1, y1, x2, y2); +} + +void picogame_tilemap_extent(picogame_tilemap_obj_t *tm, int *x1, int *y1, int *x2, int *y2) { + int tw = tm->tileset ? tm->tileset->width : 0; + int th = tm->tileset ? tm->tileset->height : 0; + *x1 = tm->x; + *y1 = tm->y; + *x2 = tm->x + (int)tm->map_w * tw; + *y2 = tm->y + (int)tm->map_h * th; +} + +void picogame_blit_tilemap( + uint16_t *buf, int region_w, int strip_top, int strip_h, int x0, + picogame_tilemap_obj_t *tm, int ox, int oy) { + picogame_bitmap_obj_t *ts = tm->tileset; + if (ts == NULL) { + return; + } + int tw = ts->width; + int th = ts->height; + int nframes = ts->frames; + + // Tilemap origin in screen coords (scene position + view offset). + int tmx = tm->x + ox; + int tmy = tm->y + oy; + + // Region in screen coords. + int rx1 = x0, ry1 = strip_top; + int rx2 = x0 + region_w, ry2 = strip_top + strip_h; + + // Tile index range overlapping the region. Tile dims are almost always powers of two (8, 16), + // where floor division is an arithmetic shift (signed >> floors toward -inf, exactly what + // floordiv does) - which skips 4 idiv per strip on the tilemap background. Non-pow2 tiles fall + // back to floordiv. (shx>=0 signals the pow2 fast path; the ctz runs twice per call, not per tile.) + int shx = (tw & (tw - 1)) ? -1 : pow2_shift((unsigned)tw); + int shy = (th & (th - 1)) ? -1 : pow2_shift((unsigned)th); + int tx_lo = (shx >= 0) ? ((rx1 - tmx) >> shx) : floordiv(rx1 - tmx, tw); + int tx_hi = (shx >= 0) ? ((rx2 - 1 - tmx) >> shx) : floordiv(rx2 - 1 - tmx, tw); + int ty_lo = (shy >= 0) ? ((ry1 - tmy) >> shy) : floordiv(ry1 - tmy, th); + int ty_hi = (shy >= 0) ? ((ry2 - 1 - tmy) >> shy) : floordiv(ry2 - 1 - tmy, th); + tx_lo = picogame_imax(tx_lo, 0); + ty_lo = picogame_imax(ty_lo, 0); + tx_hi = picogame_imin(tx_hi, (int)tm->map_w - 1); + ty_hi = picogame_imin(ty_hi, (int)tm->map_h - 1); + + const uint8_t *map = tm->map; // hoist per-tile decode invariants out of the inner loop + const uint8_t *orient = tm->orient; + int map_w = (int)tm->map_w; + for (int ty = ty_lo; ty <= ty_hi; ty++) { + int dy = tmy + ty * th; + size_t row = (size_t)ty * map_w; + for (int tx = tx_lo; tx <= tx_hi; tx++) { + size_t cell = row + tx; + uint8_t idx = map[cell]; + if (idx >= nframes) { + continue; + } + uint8_t o = orient ? orient[cell] : 0; + int dx = tmx + tx * tw; + picogame_blit_bitmap(buf, region_w, strip_h, x0, strip_top, + ts, dx, dy, idx, (o & 1) != 0, (o & 2) != 0, (o & 4) != 0, NULL); + } + } +} diff --git a/shared-module/picogame/Tilemap.h b/shared-module/picogame/Tilemap.h new file mode 100644 index 00000000000..e06e0a626b1 --- /dev/null +++ b/shared-module/picogame/Tilemap.h @@ -0,0 +1,44 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT +// +// picogame Tilemap: a grid of tile indices into a tileset Bitmap (each tile = one +// frame of the bitmap). Rendered as a Scene layer; maintains an accumulated dirty +// rectangle (screen coords) so only changed tiles/areas are repainted. + +#pragma once + +#include +#include +#include "py/obj.h" +#include "shared-module/picogame/Bitmap.h" + +typedef struct { + mp_obj_base_t base; + picogame_bitmap_obj_t *tileset; // each frame is a tile (tileset->width x height) + mp_obj_t tileset_obj; // keep alive + uint8_t *map; // map_w*map_h tile indices + mp_obj_t map_obj; // keep alive + uint8_t *orient; // map_w*map_h orientation bits (bit0 flipX, bit1 flipY, + // bit2 transpose); NULL until a tile sets an orientation + mp_obj_t orient_obj; // keep alive (lazily allocated) + uint16_t map_w, map_h; + int32_t x, y; // pixel position of tile (0,0) (int32: big maps scroll past +-32767) + int32_t dx1, dy1, dx2, dy2; // accumulated dirty rect (scene coords; int32, see x/y); x1>=x2 => empty +} picogame_tilemap_obj_t; + +void picogame_tilemap_dirty_reset(picogame_tilemap_obj_t *tm); +void picogame_tilemap_dirty_union(picogame_tilemap_obj_t *tm, int x1, int y1, int x2, int y2); +// Returns true and fills the dirty rect if non-empty, then resets it. +bool picogame_tilemap_take_dirty(picogame_tilemap_obj_t *tm, int *x1, int *y1, int *x2, int *y2); + +// On-screen bounding box of the whole map. +void picogame_tilemap_extent(picogame_tilemap_obj_t *tm, int *x1, int *y1, int *x2, int *y2); + +// Blit the tiles intersecting the strip region into buf. (ox, oy) is the view +// offset added to the tilemap position (scene space -> screen space). +void picogame_blit_tilemap( + uint16_t *buf, int region_w, int strip_top, int strip_h, int x0, + picogame_tilemap_obj_t *tm, int ox, int oy); diff --git a/shared-module/picogame/__init__.c b/shared-module/picogame/__init__.c new file mode 100644 index 00000000000..ddd700959ed --- /dev/null +++ b/shared-module/picogame/__init__.c @@ -0,0 +1,1329 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT +// +// picogame portable render core: strip-based, arbitrary-size blitting. +// Painter's order: clear strip to background, draw layers/sprites bottom-to-top. + +#include +#include +#include "py/runtime.h" +#include "shared-module/picogame/pg_compat.h" +#include "shared-module/picogame/__init__.h" +#include "shared-module/picogame/Tilemap.h" +#include "shared-module/picogame/Particles.h" +#include "shared-module/picogame/Canvas.h" +#include "shared-bindings/busdisplay/BusDisplay.h" +#include "shared-module/displayio/display_core.h" +#include "shared-bindings/displayio/__init__.h" + +// Shared dirty-rect accumulator over a contiguous int32 [x1,y1,x2,y2]. Canvas and Tilemap both end in +// `int32_t dx1,dy1,dx2,dy2`, so their public dirty fns are thin wrappers passing &self->dx1 here. +// Sentinels are the INT32 extremes (not int16) so a big-world scene coord past +-32767 px still +// accumulates correctly. +void picogame_dirty_reset(int32_t *r) { + r[0] = 0x7fffffff; + r[1] = 0x7fffffff; + r[2] = -0x7fffffff - 1; + r[3] = -0x7fffffff - 1; +} + +void picogame_dirty_union(int32_t *r, int x1, int y1, int x2, int y2) { + if (x1 < r[0]) { + r[0] = x1; + } + if (y1 < r[1]) { + r[1] = y1; + } + if (x2 > r[2]) { + r[2] = x2; + } + if (y2 > r[3]) { + r[3] = y2; + } +} + +bool picogame_dirty_take(int32_t *r, int *x1, int *y1, int *x2, int *y2) { + bool dirty = (r[0] < r[2]) && (r[1] < r[3]); + if (dirty) { + *x1 = r[0]; + *y1 = r[1]; + *x2 = r[2]; + *y2 = r[3]; + } + picogame_dirty_reset(r); + return dirty; +} + +// Halve each RGB565 channel of a wire-order pixel (50% darken, for shadow mode). +static inline uint16_t picogame_darken(uint16_t wire) { + uint16_t c = (uint16_t)((wire >> 8) | (wire << 8)); // wire -> native RGB565 + // Halve all three channels at once: >>1 shifts every channel right; the 0x7BEF mask clears the two + // bits that would bleed across channel boundaries (R's LSB into G's MSB, G's LSB into B's MSB) + + // R's now-0 top bit. Bit-identical to the per-channel r>>1/g>>1/b>>1 above, ~half the instructions. + uint16_t o = (uint16_t)((c >> 1) & 0x7BEF); + return (uint16_t)((o >> 8) | (o << 8)); // native -> wire +} + +// 4x4 ordered (Bayer) dither thresholds, 0..15. +static const uint8_t picogame_bayer4[4][4] = { + { 0, 8, 2, 10 }, + { 12, 4, 14, 6 }, + { 3, 11, 1, 9 }, + { 15, 7, 13, 5 }, +}; + +// Multiply two wire-order RGB565 pixels per channel (TINT: colour the source, keep its shading). +static inline uint16_t picogame_mul565(uint16_t a, uint16_t b) { + uint16_t ca = (uint16_t)((a >> 8) | (a << 8)); + uint16_t cb = (uint16_t)((b >> 8) | (b << 8)); + // /31 and /63 via reciprocal-multiply (bit-identical over the full product domain 0..961 / 0..3969): + // avoids a soft-divide per tinted pixel on M0+ (no HW divide); pure integer, fine on every MCU. + uint16_t r = (uint16_t)((((ca >> 11) & 0x1f) * ((cb >> 11) & 0x1f) * 529) >> 14); + uint16_t g = (uint16_t)((((ca >> 5) & 0x3f) * ((cb >> 5) & 0x3f) * 2081) >> 17); + uint16_t bl = (uint16_t)(((ca & 0x1f) * (cb & 0x1f) * 529) >> 14); + uint16_t o = (uint16_t)((r << 11) | (g << 5) | bl); + return (uint16_t)((o >> 8) | (o << 8)); +} + +// Write one opaque source pixel through the effect. (x, y) are screen coords (for DITHER). +static inline void picogame_fx_put(uint16_t *dst, uint16_t src, int x, int y, const picogame_fx_t *fx) { + if (fx == NULL) { + *dst = src; + return; + } + switch (fx->mode) { + case PICOGAME_FX_SHADOW: + *dst = picogame_darken(*dst); + break; + case PICOGAME_FX_FLASH: + *dst = fx->color; + break; + case PICOGAME_FX_TINT: + *dst = picogame_mul565(src, fx->color); // colour the sprite, keep its shading + break; + case PICOGAME_FX_DITHER: + if (picogame_bayer4[y & 3][x & 3] >= fx->level) { + *dst = src; // else: pixel skipped -> shows through + } + break; + default: + *dst = src; + break; + } +} + +// Fetch one source pixel from HOISTED scalars: the caller lifts format/data/palette/transparency +// out of the bitmap struct ONCE before its loop, so this does no per-pixel reload of bm fields (a +// `*dst` uint16_t store would otherwise force GCC to reload bm's uint16_t members every pixel). `idx` +// is the linear source offset (srow + sx). Returns false on the transparent key. Used by the scaled / +// affine / transpose paths; the unscaled fast path inlines the read directly. + +void picogame_blit_bitmap( + uint16_t *buf, int bw, int bh, int ox, int oy, + picogame_bitmap_obj_t *bm, int dx0, int dy0, int frame, bool fx, bool fy, + bool transpose, const picogame_fx_t *fxm) { + if (bm == NULL) { + return; + } + // Point fxm at a stack copy: its fields (uint16_t color) can't then alias the *dst stores, so the + // effect params stay in registers across the pixel loop instead of reloading every pixel. + picogame_fx_t fxl; + if (fxm != NULL) { + fxl = *fxm; + fxm = &fxl; + } + int sw = bm->width; + int sh = bm->height; + + // Guard the frame index: sprite.frame is a free uint8_t, so an out-of-range + // value (bad wrap / overflow in game code) would read past the sheet data. + // Wrap into [0, frames) - cheap and animation-friendly. + if (bm->frames > 1) { + if (frame >= bm->frames) { // common case is in range: pay a compare, not a divide + frame %= bm->frames; + } + } else { + frame = 0; + } + + int frame_col0 = frame * sw; + int stride0 = bm->stride; + + // Transpose path: swap source x/y (a cheap 90deg rotate when combined with flips -> all 8 + // orientations, no cos/sin/affine). The drawn footprint swaps to sh x sw. Per-pixel sampling + // (no per-row srow precompute), used only when requested; flips + fx still apply. + if (transpose) { + int dw = sh, dh = sw; // footprint swaps + int xs = picogame_imax(dx0, ox), ys = picogame_imax(dy0, oy); + int xe = picogame_imin(dx0 + dw, ox + bw), ye = picogame_imin(dy0 + dh, oy + bh); + if (xs >= xe || ys >= ye) { + return; + } + int t_fmt = bm->format; // hoist bm fields once (see src_pixel_s) + const uint8_t *t_data = bm->data; + const uint16_t *t_pal = bm->palette; + bool t_transp = bm->has_transparent; + uint16_t t_key = bm->transparent; + for (int y = ys; y < ye; y++) { + int ly = y - dy0; // -> source X (0..sw-1) + int su = fx ? sw - 1 - ly : ly; // per-row: source column is constant across the row + uint16_t *dst = buf + (y - oy) * bw + (xs - ox); + int lx = xs - dx0; + int sv = fy ? sh - 1 - lx : lx; // source row: step it, no per-pixel ternary + int svstep = fy ? -1 : 1; + for (int x = xs; x < xe; x++) { + uint16_t val; + if (src_pixel_s(t_fmt, t_data, t_pal, t_transp, t_key, + sv * stride0 + frame_col0 + su, &val)) { + picogame_fx_put(dst, val, x, y, fxm); + } + dst++; + sv += svstep; + } + } + return; + } + + int x_start = picogame_imax(dx0, ox); + int y_start = picogame_imax(dy0, oy); + int x_end = picogame_imin(dx0 + sw, ox + bw); + int y_end = picogame_imin(dy0 + sh, oy + bh); + if (x_start >= x_end || y_start >= y_end) { + return; + } + + int frame_col = frame * sw; + int stride = bm->stride; + bool transp = bm->has_transparent; + + if (bm->format == PICOGAME_FMT_PAL8) { + const uint8_t *data = bm->data; + const uint16_t *pal = bm->palette; + uint8_t key = (uint8_t)bm->transparent; + // Contract: PAL8 indices MUST be < palette length (caller's responsibility). An out-of-range + // index is undefined behaviour: it reads past the palette - usually a garbage colour, but on + // some heap layouts / platforms (e.g. ESP32-S3 heap_caps regions) it CAN fault. We deliberately + // do NOT clamp per pixel: that cost ~3 cyc/px (cmp+sbcs+ands) here, ~8% on blit-bound frames. + // + // TO RESTORE FULL BOUNDS-SAFETY (at that cost) reinstate the clamp - add `unsigned pe = + // bm->pal_entries;` here and `if (idx >= pe) { idx = 0; }` after each `idx = data[...]` in BOTH + // loops below, and the matching guard in src_pixel() (search "blit contract"). + for (int y = y_start; y < y_end; y++) { + int sy = y - dy0; + if (fy) { + sy = sh - 1 - sy; + } + int srow = sy * stride + frame_col; + uint16_t *dst = buf + (y - oy) * bw + (x_start - ox); + int sx = x_start - dx0, xstep = 1; // hoist flip_x: walk sx +/-1, no per-pixel test + if (fx) { + sx = sw - 1 - sx; + xstep = -1; + } + if (fxm == NULL) { // plain copy (most sprites): no per-pixel fx branch/call + #pragma GCC unroll 4 // hot path: unrolling the plain sprite blit is ~6% faster on M0+ (measured), +0.6KB + for (int x = x_start; x < x_end; x++) { + uint8_t idx = data[srow + sx]; + if (!transp || idx != key) { + *dst = pal[idx]; + } + dst++; + sx += xstep; + } + } else { + for (int x = x_start; x < x_end; x++) { + uint8_t idx = data[srow + sx]; + if (!transp || idx != key) { + picogame_fx_put(dst, pal[idx], x, y, fxm); + } + dst++; + sx += xstep; + } + } + } + } else { // PICOGAME_FMT_RGB565 + // bm->data is a GC-allocated Python buffer (>=4-byte aligned), so the 16-bit + // view is safe; xtensa's -Wcast-align can't see that, so silence it here. + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wcast-align" + const uint16_t *data = (const uint16_t *)bm->data; + #pragma GCC diagnostic pop + uint16_t key = bm->transparent; + for (int y = y_start; y < y_end; y++) { + int sy = y - dy0; + if (fy) { + sy = sh - 1 - sy; + } + int srow = sy * stride + frame_col; + uint16_t *dst = buf + (y - oy) * bw + (x_start - ox); + int sx = x_start - dx0, xstep = 1; // hoist flip_x: walk sx +/-1, no per-pixel test + if (fx) { + sx = sw - 1 - sx; + xstep = -1; + } + if (fxm == NULL) { // plain copy (most sprites): no per-pixel fx branch/call + if (!transp && !fx) { // opaque + not x-flipped: the row is contiguous in + // both src and dst -> one memcpy (dst may be 2-byte aligned; memcpy handles that). + memcpy(dst, &data[srow + sx], (size_t)(x_end - x_start) * 2u); + continue; + } + #pragma GCC unroll 4 // hot path: unrolling the plain sprite blit is ~6% faster on M0+ (measured), +0.6KB + for (int x = x_start; x < x_end; x++) { + uint16_t v = data[srow + sx]; + if (!transp || v != key) { + *dst = v; + } + dst++; + sx += xstep; + } + } else { + for (int x = x_start; x < x_end; x++) { + uint16_t v = data[srow + sx]; + if (!transp || v != key) { + picogame_fx_put(dst, v, x, y, fxm); + } + dst++; + sx += xstep; + } + } + } + } +} + + +void picogame_blit_bitmap_scaled( + uint16_t *buf, int bw, int bh, int ox, int oy, + picogame_bitmap_obj_t *bm, int dx0, int dy0, int frame, bool fx, bool fy, + uint16_t scale, const picogame_fx_t *fxm) { + if (bm == NULL || scale == 0) { + return; + } + picogame_fx_t fxl; // stack copy: fields don't alias *dst (see blit_bitmap) + if (fxm != NULL) { + fxl = *fxm; + fxm = &fxl; + } + int sw = bm->width, sh = bm->height; + if (bm->frames > 1) { + if (frame >= bm->frames) { // common case is in range: pay a compare, not a divide + frame %= bm->frames; + } + } else { + frame = 0; + } + int dw = (sw * scale) >> 8, dh = (sh * scale) >> 8; + if (dw <= 0 || dh <= 0) { + return; + } + int x_start = picogame_imax(dx0, ox), y_start = picogame_imax(dy0, oy); + int x_end = picogame_imin(dx0 + dw, ox + bw), y_end = picogame_imin(dy0 + dh, oy + bh); + if (x_start >= x_end || y_start >= y_end) { + return; + } + int frame_col = frame * sw, stride = bm->stride; + int s_fmt = bm->format; // hoist bm fields once (see src_pixel_s) + const uint8_t *s_data = bm->data; + const uint16_t *s_pal = bm->palette; + bool s_transp = bm->has_transparent; + uint16_t s_key = bm->transparent; + if (scale == 512 && !fx && !fy && fxm == NULL && !s_transp && s_fmt == PICOGAME_FMT_RGB565 + && (((uintptr_t)s_data & 1) == 0)) { + // 2x integer upscale fast path - the half-res-canvas genre's per-frame blit (a full-screen + // RGB565 bitmap shown through a scale-2 sprite). At scale 512 the DDA step is exactly 2^15, + // so sampling collapses to (rel >> 1): write each source pixel twice, and when two dest rows + // share a source row, memcpy the second from the first (half the reads). Byte-exact vs the + // generic loop (host-verified over 300k random windows/clips/parities). + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wcast-align" + const uint16_t *sd16 = (const uint16_t *)s_data; + #pragma GCC diagnostic pop + int prev_sy = -1; + uint16_t *prev_row = NULL; + int nwin = x_end - x_start; + for (int y = y_start; y < y_end; y++) { + int sy = (y - dy0) >> 1; + uint16_t *drow = buf + (y - oy) * bw + (x_start - ox); + if (sy == prev_sy && prev_row != NULL) { + memcpy(drow, prev_row, (size_t)nwin * 2); + } else { + const uint16_t *srow = sd16 + sy * stride + frame_col; + int rel = x_start - dx0; + uint16_t *d = drow; + int n = nwin; + if (rel & 1) { // leading odd dest column + *d++ = srow[rel >> 1]; + rel++; + n--; + } + const uint16_t *sp = srow + (rel >> 1); + while (n >= 2) { + uint16_t v = *sp++; + d[0] = v; + d[1] = v; + d += 2; + n -= 2; + } + if (n) { + *d = *sp; + } + } + prev_sy = sy; + prev_row = drow; + } + return; + } + if (scale >= 512 && (scale & (scale - 1)) == 0 && !fx && !fy && fxm == NULL && !s_transp + && s_fmt == PICOGAME_FMT_PAL8) { + // Integer upscale, PAL8 - the same idea as the RGB565 2x path above (which exists for the + // half-res-canvas genre), for the format the engine's art actually uses, and for ANY integer + // factor rather than just 2. At an integer scale the DDA collapses: every source pixel maps + // to exactly `nrep` dest pixels and every source row to `nrep` dest rows, so a row is + // rasterized once and its repeats are memcpy'd. + // + // WHY IT MATTERS - it is a RAM technique, not just a speed one: art kept at 1/nrep and + // upscaled costs 1/nrep^2 of the bitmap. A 320x100 PAL8 parallax band is 31.2 kB at 1:1 but + // 7.8 kB at half size, and RP2040 has 25-40 kB of heap. Making the upscale cheap is what + // makes that trade attractive instead of merely possible. + // + // Transparency is deliberately NOT handled here: a skipped pixel must keep ITS OWN row's + // background, so the repeats cannot be memcpy'd, and measurement showed the remaining win + // (0.1-0.2 ms on the small transparent sprites that actually exist) did not justify the flash. + const int nrep = scale >> 8; + const int dy_rel = y_start - dy0, dx_rel = x_start - dx0; + int sy = dy_rel / nrep; + int rem = nrep - (dy_rel % nrep); // dest rows this source row still owns + const int sx0 = dx_rel / nrep, hoff = dx_rel % nrep; + const int nwin = x_end - x_start; + bool draw = true; + uint16_t *rep_from = NULL; + for (int y = y_start; y < y_end; y++) { + uint16_t *drow = buf + (y - oy) * bw + (x_start - ox); + if (draw) { + const uint8_t *sp = s_data + sy * stride + frame_col + sx0; + uint16_t *d = drow; + int n = nwin; + int first = nrep - hoff; // the leading source pixel may be partly clipped + if (first > n) { + first = n; + } + uint16_t c = s_pal[*sp++]; + for (int i = 0; i < first; i++) { + *d++ = c; + } + n -= first; + if (nrep == 2) { // the common case, unrolled: two stores, no counter + while (n >= 2) { + c = s_pal[*sp++]; + d[0] = c; + d[1] = c; + d += 2; + n -= 2; + } + } else { + while (n >= nrep) { + c = s_pal[*sp++]; + for (int i = 0; i < nrep; i++) { + *d++ = c; + } + n -= nrep; + } + } + if (n) { // trailing partial group + c = s_pal[*sp]; + while (n--) { + *d++ = c; + } + } + rep_from = drow; + draw = false; + } else { + memcpy(drow, rep_from, (size_t)nwin * 2); + } + if (--rem == 0) { + sy++; + rem = nrep; + draw = true; + } + } + return; + } + uint32_t step = ((uint32_t)1 << 24) / scale; // source px per dest px, 16.16 + // No per-row sy>=sh / per-pixel sx>=sw clamp: with dw=(sd*scale)>>8 and step=floor(2^24/scale), + // the sampled index ((dw-1)*step)>>16 provably never reaches the source dimension (exhaustively + // verified over the ENTIRE uint16 x uint16 (scale, dim) domain, 0 violations), and xacc> 16); + if (fy) { + sy = sh - 1 - sy; + } + int srow = sy * stride + frame_col; + uint16_t *drow = buf + (y - oy) * bw; + uint32_t xacc = (uint32_t)(x_start - dx0) * step; + for (int x = x_start; x < x_end; x++) { + int sx = (int)(xacc >> 16); + xacc += step; + if (fx) { + sx = sw - 1 - sx; + } + uint16_t val; + if (src_pixel_s(s_fmt, s_data, s_pal, s_transp, s_key, srow + sx, &val)) { + picogame_fx_put(&drow[x - ox], val, x, y, fxm); + } + } + } +} + +// Quarter-wave Q15 sine table (0..90 deg) -> fixed-point trig for the rotation setup, so the +// affine path needs no float `sinf`/`cosf` (RP2040 has no FPU). cos(d) = sin(d+90). +static const int16_t pg_sin_q15_quad[91] = { + 0, 572, 1144, 1715, 2286, 2856, 3425, 3993, 4560, 5126, + 5690, 6252, 6813, 7371, 7927, 8481, 9032, 9580, 10126, 10668, + 11207, 11743, 12275, 12803, 13328, 13848, 14364, 14876, 15383, 15886, + 16383, 16876, 17364, 17846, 18323, 18794, 19260, 19720, 20173, 20621, + 21062, 21497, 21925, 22347, 22762, 23170, 23571, 23964, 24351, 24730, + 25101, 25465, 25821, 26169, 26509, 26841, 27165, 27481, 27788, 28087, + 28377, 28659, 28932, 29196, 29451, 29697, 29934, 30162, 30381, 30591, + 30791, 30982, 31163, 31335, 31498, 31650, 31794, 31927, 32051, 32165, + 32269, 32364, 32448, 32523, 32587, 32642, 32687, 32722, 32747, 32762, + 32767, +}; +static int32_t pg_sin_q15(int deg) { + deg %= 360; + if (deg < 0) { + deg += 360; + } + if (deg <= 90) { + return pg_sin_q15_quad[deg]; + } + if (deg <= 180) { + return pg_sin_q15_quad[180 - deg]; + } + if (deg <= 270) { + return -pg_sin_q15_quad[deg - 180]; + } + return -pg_sin_q15_quad[360 - deg]; +} +static int32_t pg_cos_q15(int deg) { + return pg_sin_q15(deg + 90); +} + +// sin of a Q16 degree angle: lerp between whole-degree LUT entries. The racing-road curvature is +// DOUBLE-integrated over ~170 rows, which amplifies whole-degree quantization into visible pixels +// (host-measured 9 px); one lerp per curvature eval brings the road within 1 px of the float original. +static int32_t pg_sin_q15_lerp(int64_t deg_q16) { + int d0 = (int)(deg_q16 >> 16); + int32_t frac = (int32_t)(deg_q16 & 0xFFFF); + int32_t a = pg_sin_q15(d0); + return a + (int32_t)(((int64_t)(pg_sin_q15(d0 + 1) - a) * frac) >> 16); +} + +// One racing-road frame's curve pass: the bottom-up curvature accumulator + per-row integer edges +// (the OutRun-genre "compute_road" loop - profiled at ~8-10 ms of Python on picobike; this is the +// batch-boundary rule in action: one C call does the frame's whole row loop). Fixed-point throughout: +// cx/ddx accumulate in Q16, curvature = two LUT sines of the world distance. cfg (int32[7]): +// [f1_q20, f2_q20, amp1k_q16, amp2k_q16, world_step, curve_step, d_row_off] - frequencies in +// Q20 degrees/world-unit (Q16 phase-drifts over a long run), amplitudes premultiplied by the +// per-row gain k, curvature re-evaluated every curve_step rows (it varies slowly). Edge stores use +// trunc-toward-zero to match the Python original's int(). Host-proven <=1 px absolute AND row-delta +// smoothness vs the float reference over 8k+ frames (road_edges_test.c). +void picogame_road_edges(int16_t *rl, int16_t *rr, const int32_t *hw_q16, int n, + int32_t cx_q16, int32_t dist, const int32_t *cfg) { + int32_t f1 = cfg[0], f2 = cfg[1], a1k = cfg[2], a2k = cfg[3]; + int32_t wstep = cfg[4], cstep = cfg[5], drow = cfg[6]; + int32_t cx = cx_q16, ddx = 0, ck = 0; + int cnt = 0; + for (int i = n - 1; i >= 0; i--) { + if (cnt == 0) { + int32_t d = dist + (drow - i) * wstep; + ck = (int32_t)(((int64_t)pg_sin_q15_lerp(((int64_t)d * f1) >> 4) * a1k) >> 15) + + (int32_t)(((int64_t)pg_sin_q15_lerp(((int64_t)d * f2) >> 4) * a2k) >> 15); + cnt = cstep; + } + cnt--; + ddx += ck; + cx += ddx; + int32_t vl = cx - hw_q16[i], vr = cx + hw_q16[i]; + rl[i] = (int16_t)(vl >= 0 ? (vl >> 16) : -((-vl) >> 16)); + rr[i] = (int16_t)(vr >= 0 ? (vr >> 16) : -((-vr) >> 16)); + } +} + + +// Forward-transform a w*h rect's 4 corners through (integer pivot, 8.8 scale, Q15 rotation) and +// return the screen-space AABB. INTEGER (Q16) - measured ~40x faster than the old float version on +// the M0+ flagship (soft-float), and faster on every target (measured affine_q16 vs affine_float). +// Runs once per rotated sprite (not per pixel). Each corner is FLOORED (arithmetic >>23 = /(256*32768)), +// so the returned box is <= the true min and the callers' +1/+2 keep the box CONTAINING the sprite +// (the affine blitter clips per pixel anyway, so a >=1px-too-large box only repaints, never clips). +static void corners_bbox(int sw, int sh, int px, int py, int pivx, int pivy, + int32_t scale, int32_t cos_q, int32_t sin_q, int *minx, int *miny, int *maxx, int *maxy) { + int nx = 1 << 30, xx = -(1 << 30), ny = 1 << 30, xy = -(1 << 30); + int cxs[4] = { 0, sw, 0, sw }, cys[4] = { 0, 0, sh, sh }; + for (int k = 0; k < 4; k++) { + int64_t du = (int64_t)(cxs[k] - pivx) * scale; // (corner - pivot) in pixels<<8 + int64_t dv = (int64_t)(cys[k] - pivy) * scale; + int X = px + (int)((du * cos_q - dv * sin_q) >> 23); // >>23 = /(256 * 32768): drop the 8.8 + Q15 + int Y = py + (int)((du * sin_q + dv * cos_q) >> 23); + if (X < nx) { + nx = X; + } + if (X > xx) { + xx = X; + } + if (Y < ny) { + ny = Y; + } + if (Y > xy) { + xy = Y; + } + } + *minx = nx; + *miny = ny; + *maxx = xx; + *maxy = xy; +} + +// Fill the sprite's affine cache (position-relative bbox + 16.16 inverse-map steps) if stale. +// The trig LUT, the 4-corner bbox and the TWO SOFTWARE DIVIDES below used to re-run once per +// strip a rotated sprite touched (~6x/frame at strip_h=8) plus once for the dirty-rect AABB; +// now once per angle/scale/bitmap/anchor change (those setters clear xf_valid). +static void sprite_xform_fill(picogame_sprite_obj_t *s) { + if (s->xf_valid) { + return; + } + int w = (s->bitmap != NULL) ? s->bitmap->width : 0; + int h = (s->bitmap != NULL) ? s->bitmap->height : 0; + int pivx = ((int)s->anchor_x * w) >> 8, pivy = ((int)s->anchor_y * h) >> 8; + int32_t cos_q = pg_cos_q15(s->angle), sin_q = pg_sin_q15(s->angle); // Q15 LUT trig + int minx, miny, maxx, maxy; + corners_bbox(w, h, 0, 0, pivx, pivy, (int32_t)s->scale, cos_q, sin_q, + &minx, &miny, &maxx, &maxy); + // Saturate into the int16 cache fields: an extreme scale x size (public scale allows + // ~256x) could exceed +-32767; a saturated bbox only over/under-covers the clip - the + // blitter clips per strip anyway - instead of wrapping into a wrong-sign rect. + s->xf_minx = (int16_t)(minx < -32768 ? -32768 : (minx > 32767 ? 32767 : minx)); + s->xf_miny = (int16_t)(miny < -32768 ? -32768 : (miny > 32767 ? 32767 : miny)); + s->xf_maxx = (int16_t)(maxx < -32768 ? -32768 : (maxx > 32767 ? 32767 : maxx)); + s->xf_maxy = (int16_t)(maxy < -32768 ? -32768 : (maxy > 32767 ? 32767 : maxy)); + // inverse map (16.16 fixed-point): u = pivx + (ic*(X-px) + is*(Y-py)); + // v = pivy + (-is*(X-px) + ic*(Y-py)) + // ic = (cs/sf)*65536 = cos_q15 * 512 / scale - computed in pure integer (no float). + int32_t nic = cos_q * 512, nis = sin_q * 512; + int sc = (int)s->scale; + if (sc < 1) { + sc = 1; // scale is setter-clamped >= 1; belt for a zeroed struct + } + s->xf_ic = (nic >= 0 ? nic + sc / 2 : nic - sc / 2) / sc; + s->xf_is = (nis >= 0 ? nis + sc / 2 : nis - sc / 2) / sc; + s->xf_valid = 1; +} + +void picogame_blit_bitmap_affine( + uint16_t *buf, int bw, int bh, int ox, int oy, + picogame_bitmap_obj_t *bm, int px, int py, int pivx, int pivy, + int frame, bool fx, bool fy, + int minx, int miny, int maxx, int maxy, int32_t ic, int32_t is, + const picogame_fx_t *fxm) { + if (bm == NULL) { + return; + } + picogame_fx_t fxl; // stack copy: fields don't alias *dst (see blit_bitmap) + if (fxm != NULL) { + fxl = *fxm; + fxm = &fxl; + } + int sw = bm->width, sh = bm->height; + if (bm->frames > 1) { + if (frame >= bm->frames) { // common case is in range: pay a compare, not a divide + frame %= bm->frames; + } + } else { + frame = 0; + } + int frame_col = frame * sw, stride = bm->stride; + int a_fmt = bm->format; // hoist bm fields once (see src_pixel_s) + const uint8_t *a_data = bm->data; + const uint16_t *a_pal = bm->palette; + bool a_transp = bm->has_transparent; + uint16_t a_key = bm->transparent; + int x_start = picogame_imax(minx, ox), y_start = picogame_imax(miny, oy); + int x_end = picogame_imin(maxx + 1, ox + bw), y_end = picogame_imin(maxy + 1, oy + bh); + if (x_start >= x_end || y_start >= y_end) { + return; + } + // Fold flip_x/flip_y into the inverse map ONCE, so the inner loop samples the FINAL source coord + // directly - no per-pixel `sw-1-iu`/`sh-1-iv` and no fx/fy branch (one loop variant, fewer live + // values -> fewer register spills). Exact: mirroring u is u'=(sw-1)-u; through the 16.16 floor that + // is `((sw-1-pivx)<<16 + 0xFFFF) - (ic*dxf + is*dyf)`, i.e. negate the u steps + bias the pivot by + // 0xFFFF (so floor(u') == (sw-1) - floor(u) for every in-range pixel). Bit-identical output. + int32_t uxc = ic, uyc = is, upiv = (int32_t)pivx << 16; // u = upiv + uxc*dxf + uyc*dyf + int32_t vxc = -is, vyc = ic, vpiv = (int32_t)pivy << 16; // v = vpiv + vxc*dxf + vyc*dyf + if (fx) { + uxc = -ic; + uyc = -is; + upiv = ((int32_t)(sw - 1 - pivx) << 16) + 0xFFFF; + } + if (fy) { + vxc = is; + vyc = -ic; + vpiv = ((int32_t)(sh - 1 - pivy) << 16) + 0xFFFF; + } + // 32-bit 16.16 accumulators (cheaper than 64-bit on the M0+). Peak magnitude stays well within + // int32 for any sane sprite on a handheld screen. The bounds check keeps an out-of-range sample + // memory-safe (at worst a skipped pixel), never a crash. + for (int y = y_start; y < y_end; y++) { + int dyf = y - py; + int dxf = x_start - px; + int32_t uacc = upiv + uxc * dxf + uyc * dyf; + int32_t vacc = vpiv + vxc * dxf + vyc * dyf; + uint16_t *drow = buf + (y - oy) * bw; + for (int x = x_start; x < x_end; x++) { + int su = uacc >> 16, sv = vacc >> 16; // already the flipped source coords + uacc += uxc; + vacc += vxc; + if (su >= 0 && su < sw && sv >= 0 && sv < sh) { + uint16_t val; + if (src_pixel_s(a_fmt, a_data, a_pal, a_transp, a_key, + sv * stride + frame_col + su, &val)) { + picogame_fx_put(&drow[x - ox], val, x, y, fxm); + } + } + } + } +} + +void picogame_sprite_aabb(const picogame_sprite_obj_t *s, int *x1, int *y1, int *x2, int *y2) { + int w = (s->bitmap != NULL) ? s->bitmap->width : 0; + int h = (s->bitmap != NULL) ? s->bitmap->height : 0; + if (s->angle == 0) { + int sw = (w * s->scale) >> 8, sh = (h * s->scale) >> 8; + if ((s->flags & PICOGAME_SPR_TRANSPOSE) && s->scale == 256) { // transpose only on the fast + int t = sw; // path (scale==256); scaled blitter ignores it, so + sw = sh; // swapping here for scale!=256 would mistrack -> trail + sh = t; + } + int tx = (s->x >> 8) - ((int)s->anchor_x * sw >> 8); + int ty = (s->y >> 8) - ((int)s->anchor_y * sh >> 8); + *x1 = tx; + *y1 = ty; + *x2 = tx + sw; + *y2 = ty + sh; + return; + } + // cache-fill mutates only the derived xf_* fields - logically const for callers + sprite_xform_fill((picogame_sprite_obj_t *)s); + int px = s->x >> 8, py = s->y >> 8; + *x1 = px + s->xf_minx - 1; + *y1 = py + s->xf_miny - 1; + *x2 = px + s->xf_maxx + 2; + *y2 = py + s->xf_maxy + 2; +} + +// TINT on a PAL8 sprite is baked into a stack palette (see blit_sprite) when the palette fits this +// cap, else it falls back to the per-pixel tint. 64 entries = 128 B of transient stack. +#define PICOGAME_TINT_PAL_CAP 64 + +// clip_x/clip_y = the strip buffer's screen origin; vx/vy = view offset added to +// the sprite's scene position to get its screen position. +static void blit_sprite(uint16_t *buf, int bw, int bh, int clip_x, int clip_y, + picogame_sprite_obj_t *spr, int vx, int vy) { + bool fx = (spr->flags & PICOGAME_SPR_FLIP_X) != 0; + bool fy = (spr->flags & PICOGAME_SPR_FLIP_Y) != 0; + bool tr = (spr->flags & PICOGAME_SPR_TRANSPOSE) != 0; // 90deg transpose (fast path only) + // One effect at a time (dither > flash > tint > shadow priority); NULL = no effect (fast path). + picogame_fx_t fxm = { PICOGAME_FX_NONE, 0, 0 }; + if (spr->flags & PICOGAME_SPR_DITHER) { + fxm.mode = PICOGAME_FX_DITHER; + fxm.level = spr->dither; + } else if (spr->flags & PICOGAME_SPR_FLASH) { + fxm.mode = PICOGAME_FX_FLASH; + fxm.color = spr->flash_color; + } else if (spr->flags & PICOGAME_SPR_TINT) { + fxm.mode = PICOGAME_FX_TINT; + fxm.color = spr->flash_color; // shared colour field (flash/tint exclusive) + } else if (spr->flags & PICOGAME_SPR_SHADOW) { + fxm.mode = PICOGAME_FX_SHADOW; + } + const picogame_fx_t *fxp = (fxm.mode == PICOGAME_FX_NONE) ? NULL : &fxm; + // TINT of a PAL8 sprite is a pure function of the palette index -> bake it into a stack palette + // ONCE and blit plain, instead of picogame_mul565 per pixel (~4x on tinted PAL8 sprites; the + // dominant sprite format). Small palettes only; larger ones keep the per-pixel tint. Transparency + // is keyed on the INDEX before the palette lookup, so the (unused) tinted key entry is harmless. + picogame_bitmap_obj_t *bmuse = spr->bitmap; + picogame_bitmap_obj_t bmtint; + uint16_t tpal[PICOGAME_TINT_PAL_CAP]; + if (fxp != NULL && fxm.mode == PICOGAME_FX_TINT && bmuse != NULL && + bmuse->format == PICOGAME_FMT_PAL8 && bmuse->pal_entries <= PICOGAME_TINT_PAL_CAP) { + for (int i = 0; i < bmuse->pal_entries; i++) { + tpal[i] = picogame_mul565(bmuse->palette[i], fxm.color); + } + bmtint = *bmuse; // shallow copy; override only the palette + bmtint.palette = tpal; + bmuse = &bmtint; + fxp = NULL; // tint now baked in -> plain (fast) blit + } + if (spr->angle == 0 && spr->scale == 256) { + int tx, ty; + picogame_sprite_topleft(spr, &tx, &ty); + picogame_blit_bitmap(buf, bw, bh, clip_x, clip_y, bmuse, + tx + vx, ty + vy, spr->frame, fx, fy, tr, fxp); + } else if (spr->angle == 0) { + int tx, ty; + picogame_sprite_topleft(spr, &tx, &ty); + picogame_blit_bitmap_scaled(buf, bw, bh, clip_x, clip_y, bmuse, + tx + vx, ty + vy, spr->frame, fx, fy, spr->scale, fxp); + } else { + sprite_xform_fill(spr); // once per angle/scale change, not per strip + int w = (spr->bitmap != NULL) ? spr->bitmap->width : 0; + int h = (spr->bitmap != NULL) ? spr->bitmap->height : 0; + int pivx = ((int)spr->anchor_x * w) >> 8, pivy = ((int)spr->anchor_y * h) >> 8; + int px = (spr->x >> 8) + vx, py = (spr->y >> 8) + vy; + picogame_blit_bitmap_affine(buf, bw, bh, clip_x, clip_y, bmuse, + px, py, pivx, pivy, spr->frame, fx, fy, + px + spr->xf_minx, py + spr->xf_miny, px + spr->xf_maxx, py + spr->xf_maxy, + spr->xf_ic, spr->xf_is, fxp); + } +} + +mp_obj_t picogame_blit_strip_layers( + uint16_t *buf, int region_w, int strip_top, int strip_h, int x0, + mp_obj_t *items, uint8_t *kinds, size_t n, uint16_t background, int ox, int oy) { + mp_obj_t pending = MP_OBJ_NULL; // a BaseException latched from a StripDraw callback + int npix = region_w * strip_h; + if (background == 0) { + memset(buf, 0, (size_t)npix * 2); // common case (black/clear) -> fast bulk clear + } else { + // word-fill: two packed pixels per uint32 (half the stores). CAUTION: this composites into + // BOTH a GC strip buffer (SPI path, >=4-byte aligned) AND, on framebuffer targets, a raw row + // pointer fb + sy*stride + x0 that starts at an ODD pixel when x0 is odd -> only 2-byte + // aligned. GCC lowers the fill to STRD/STM, which raise an unaligned UsageFault on Cortex-M + // even with CCR.UNALIGN_TRP clear (STRD/STM always require word alignment). So peel one + // leading pixel to reach 4-byte alignment (mirrors picogame_fb_to_native), bulk word-fill, + // then an odd trailing pixel. + uint32_t w = (uint32_t)background | ((uint32_t)background << 16); + int i = 0; + if (npix > 0 && ((uintptr_t)buf & 2u) != 0) { + buf[0] = background; + i = 1; + } + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wcast-align" + uint32_t *w32 = (uint32_t *)(buf + i); // now 4-byte aligned + #pragma GCC diagnostic pop + int nw = (npix - i) >> 1; + for (int k = 0; k < nw; k++) { + w32[k] = w; + } + i += nw << 1; + if (i < npix) { // odd trailing pixel + buf[i] = background; + } + } + for (size_t i = 0; i < n; i++) { + uint8_t raw = (kinds != NULL) ? kinds[i] : PICOGAME_KIND_SPRITE; + uint8_t kind = raw & PICOGAME_KIND_MASK; + // Fixed (HUD) items are drawn in screen space -> no view offset. + int iox = (raw & PICOGAME_KIND_FIXED) ? 0 : ox; + int ioy = (raw & PICOGAME_KIND_FIXED) ? 0 : oy; + if (kind == PICOGAME_KIND_TILEMAP) { + picogame_blit_tilemap(buf, region_w, strip_top, strip_h, x0, + MP_OBJ_TO_PTR(items[i]), iox, ioy); + } else if (kind == PICOGAME_KIND_PARTICLES) { + picogame_blit_particles(buf, region_w, strip_top, strip_h, x0, + MP_OBJ_TO_PTR(items[i]), iox, ioy); + } else if (kind == PICOGAME_KIND_CANVAS) { + picogame_blit_canvas(buf, region_w, strip_top, strip_h, x0, + MP_OBJ_TO_PTR(items[i]), iox, ioy); + } else if (kind == PICOGAME_KIND_STRIPDRAW) { + // Immediate mode: hand the callback a view of the part of THIS strip that + // overlaps the layer's rect (so it can only paint within its rect - a + // full-view fill stays inside it). Skip strips the rect doesn't touch. + picogame_stripdraw_obj_t *sd = MP_OBJ_TO_PTR(items[i]); + int ry0 = sd->y, ry1 = sd->y + sd->h; + int s0 = strip_top > ry0 ? strip_top : ry0; // first screen row to draw + int st_end = strip_top + strip_h; + int s1 = st_end < ry1 ? st_end : ry1; // one past last screen row + if (s0 >= s1) { + continue; // strip is outside the layer + } + picogame_canvas_obj_t *v = MP_OBJ_TO_PTR(sd->view); + v->data = buf + (s0 - strip_top) * region_w; // view row 0 == screen row s0 + v->w = region_w; + v->h = s1 - s0; + v->x = 0; + v->y = 0; + v->has_transparent = false; + mp_obj_t cbargs[5] = { + sd->view, + MP_OBJ_NEW_SMALL_INT(x0), + MP_OBJ_NEW_SMALL_INT(s0), + MP_OBJ_NEW_SMALL_INT(region_w), + MP_OBJ_NEW_SMALL_INT(s1 - s0), + }; + // We're inside an open display bus transaction here, so a raised + // exception must NOT unwind past it (that would wedge the bus / leave a + // DMA running). Catch it, keep the transaction intact, and latch so the + // traceback prints once instead of every strip every frame. + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_call_function_n_kw(sd->callback, 5, 0, cbargs); + nlr_pop(); + } else { + mp_obj_t exc = MP_OBJ_FROM_PTR(nlr.ret_val); + if (!mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(mp_obj_get_type(exc)), + MP_OBJ_FROM_PTR(&mp_type_Exception))) { + // A BaseException (KeyboardInterrupt / ReloadException / SystemExit) must reach the + // supervisor - latch it and stop; the caller re-raises it once the display + // transaction has safely closed, so Ctrl-C and USB auto-reload actually work. + pending = exc; + break; + } + if (!sd->faulted) { + sd->faulted = true; + mp_obj_print_exception(&mp_plat_print, exc); + } + } + } else if (kind == PICOGAME_KIND_TRIANGLES) { + // Retained screen-space triangle batch: pure C per strip (no Python callback, + // so this path needs no Python re-entry mid-compose). Cheap band + // reject vs THIS strip, then the Canvas rasteriser through a stack view over + // the strip buffer. Screen-space by design: the view offset is not applied. + picogame_triangles_obj_t *t = MP_OBJ_TO_PTR(items[i]); + picogame_canvas_obj_t v; + v.data = buf; + v.data_obj = MP_OBJ_NULL; + v.w = region_w; + v.h = strip_h; + v.x = 0; + v.y = 0; + v.transparent = 0; + v.has_transparent = false; + picogame_canvas_dirty_reset(&v); + int xo = -x0; + int yo = -strip_top; + picogame_fill_triangle_batch(&v, t->verts, t->colors, t->count, xo, yo); + } else { + picogame_sprite_obj_t *spr = MP_OBJ_TO_PTR(items[i]); + if (!(spr->flags & PICOGAME_SPR_VISIBLE)) { + continue; + } + blit_sprite(buf, region_w, strip_h, x0, strip_top, spr, iox, ioy); + } + } + return pending; +} + +bool picogame_strip_begin( + picogame_output_t *display, + int *x0p, int *y0p, int *x1p, int *y1p, size_t buffer_pixels, + int *region_w, int *strip_h) { + // Clamp the window to the panel (post-rotation w/h): an out-of-range region makes the controller + // wrap/garble rows. Callers usually pass clamped dirty rects, but StripDraw / direct render_region + // can hand us a rect that runs off the panel. Clamp AND write back through the pointers so the + // caller's strip loop + blit origin use the SAME clamped bounds (else only the GRAM window is + // clamped while the data loop still pushes off-panel rows -> the wrap/garble we're preventing). + int pw = display->core.width, ph = display->core.height; + int x0 = *x0p, y0 = *y0p, x1 = *x1p, y1 = *y1p; + if (x0 < 0) { + x0 = 0; + } + if (y0 < 0) { + y0 = 0; + } + if (x1 > pw) { + x1 = pw; + } + if (y1 > ph) { + y1 = ph; + } + *x0p = x0; + *y0p = y0; + *x1p = x1; + *y1p = y1; + int rw = x1 - x0; + int rh = y1 - y0; + if (rw <= 0 || rh <= 0) { + return false; + } + int sh = (int)(buffer_pixels / (size_t)rw); + if (sh < 1) { + mp_raise_ValueError(MP_ERROR_TEXT("buffer too small")); + } + if (sh > rh) { + sh = rh; + } + + displayio_area_t area; + area.x1 = x0; + area.y1 = y0; + area.x2 = x1; + area.y2 = y1; + area.next = NULL; + displayio_display_bus_set_region_to_update(&display->bus, &display->core, &area); + + while (!displayio_display_bus_begin_transaction(&display->bus)) { + RUN_BACKGROUND_TASKS; + } + display->bus.send(display->bus.bus, DISPLAY_COMMAND, + CHIP_SELECT_TOGGLE_EVERY_BYTE, &display->write_ram_command, 1); + + *region_w = rw; + *strip_h = sh; + return true; +} + +// --- output transport seam (busdisplay backend): the ONLY per-strip display ops the generic +// picogame_render_region orchestrator below touches, so a non-CircuitPython port (MicroPython +// framebuf/SPI) swaps just strip_begin + these two + set_invert/set_pixel_format. See __init__.h. +static inline void picogame_out_strip_send(picogame_output_t *display, const uint8_t *data, size_t nbytes) { + display->bus.send(display->bus.bus, DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, data, nbytes); +} +static inline void picogame_out_strip_end(picogame_output_t *display) { + displayio_display_bus_end_transaction(&display->bus); +} + +void picogame_render_region( + picogame_output_t *display, + mp_obj_t *items, uint8_t *kinds, size_t n, + uint16_t *buffer, size_t buffer_pixels, + int16_t x0, int16_t y0, int16_t x1, int16_t y1, + uint16_t background, int ox, int oy) { + + int region_w, strip_h; + int cx0 = x0, cy0 = y0, cx1 = x1, cy1 = y1; // strip_begin clamps these to the panel in place + if (!picogame_strip_begin(display, &cx0, &cy0, &cx1, &cy1, buffer_pixels, ®ion_w, &strip_h)) { + return; + } + for (int sy = cy0; sy < cy1; sy += strip_h) { + int sh = picogame_imin(strip_h, cy1 - sy); + mp_obj_t exc = picogame_blit_strip_layers(buffer, region_w, sy, sh, cx0, items, kinds, n, background, ox, oy); + picogame_out_strip_send(display, (uint8_t *)buffer, region_w * sh * 2); + if (exc != MP_OBJ_NULL) { // a StripDraw callback raised a BaseException: close the + picogame_out_strip_end(display); // bus, then re-raise (Ctrl-C / reload) + nlr_raise(MP_OBJ_TO_PTR(exc)); + } + } + picogame_out_strip_end(display); +} + +// Toggle the panel's hardware colour inversion (INVON 0x21 / INVOFF 0x20). Instant, sends NO +// pixel data - a brief invert is a free full-screen "flash" (a 1-bit negative hit look). +void picogame_set_invert(picogame_output_t *display, bool on) { + uint8_t cmd = on ? 0x21 : 0x20; + while (!displayio_display_bus_begin_transaction(&display->bus)) { + RUN_BACKGROUND_TASKS; + } + display->bus.send(display->bus.bus, DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, &cmd, 1); + displayio_display_bus_end_transaction(&display->bus); +} + +#if CIRCUITPY_PICOGAME_RGB444 +// The RGB444 machinery (this + pack_rgb444 + the Display rgb444 path) is compiled in ONLY when a +// board sets CIRCUITPY_PICOGAME_RGB444=1. It is a transfer-bound-only win (on a CPU-balanced panel +// like the PicoPad's the pack ~= the SPI byte saving, so it loses); default off (port ?= 0) until +// multi-platform experience justifies flipping the default. See pack_rgb444's measured-optimal note. +// Set the panel pixel format (COLMOD 0x3A): rgb444 -> 12-bit RGB444 (0x53), else 16-bit RGB565 +// (0x55). Asserting it on every Display construct also recovers from a previous program that left +// the panel in the other format (survives soft reset). +void picogame_set_pixel_format(picogame_output_t *display, bool rgb444) { + uint8_t cmd = 0x3A; + uint8_t param = rgb444 ? 0x53 : 0x55; + while (!displayio_display_bus_begin_transaction(&display->bus)) { + RUN_BACKGROUND_TASKS; + } + display->bus.send(display->bus.bus, DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, &cmd, 1); + display->bus.send(display->bus.bus, DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, ¶m, 1); + displayio_display_bus_end_transaction(&display->bus); +} + +// Pack a strip of `npix` (must be even) WIRE-order RGB565 pixels IN-PLACE to ST7789 12-bit RGB444 +// (2 px -> 3 bytes): R0G0 / B0R1 / G1B1. Returns the packed byte count. Cuts SPI traffic ~25%. +// In-place is safe: the write offset (1.5*i) always trails the read offset (2*i). +size_t picogame_pack_rgb444(uint16_t *buf, size_t npix) { + // Pack wire RGB565 -> ST7789 12-bit RGB444 (2 px -> 3 bytes) in place; the write offset (1.5*i) + // always trails the read offset (2*i). NB: the byte-swap to native order is LOAD-BEARING, not + // waste - it nibble-aligns the channels so each extracts in ~2 ops; extracting straight from the + // wire value makes GREEN (split across the byte boundary) cost ~5 ops and is SLOWER on device + // (measured, M0+). Likewise do NOT wide-unroll (8 registers -> spills). This tight form is the + // measured-optimal pack; RGB444 still loses to RGB565 on a CPU-balanced panel (pack ~= the SPI + // byte saving), so it is a transfer-bound-only option (default off). + uint8_t *out = (uint8_t *)buf; + size_t o = 0; + for (size_t i = 0; i + 1 < npix; i += 2) { + uint32_t w0 = buf[i], w1 = buf[i + 1]; + uint32_t n0 = (w0 >> 8) | (w0 << 8); // wire -> native RGB565 + uint32_t n1 = (w1 >> 8) | (w1 << 8); + uint8_t r0 = (n0 >> 12) & 0xF, g0 = (n0 >> 7) & 0xF, b0 = (n0 >> 1) & 0xF; + uint8_t r1 = (n1 >> 12) & 0xF, g1 = (n1 >> 7) & 0xF, b1 = (n1 >> 1) & 0xF; + out[o++] = (r0 << 4) | g0; + out[o++] = (b0 << 4) | r1; + out[o++] = (g1 << 4) | b1; + } + return o; +} +#endif // CIRCUITPY_PICOGAME_RGB444 + +void picogame_render( + picogame_output_t *display, + mp_obj_t *items, size_t n, + uint16_t *buffer, size_t buffer_pixels, + int16_t x0, int16_t y0, int16_t x1, int16_t y1, + uint16_t background) { + picogame_render_region(display, items, NULL, n, buffer, buffer_pixels, + x0, y0, x1, y1, background, 0, 0); +} + +#if CIRCUITPY_PICOGAME_FRAMEBUFFER +// Full-frame RAM-framebuffer backend. Same layered compositor as the SPI strip path +// (picogame_blit_strip_layers -> identical kinds dispatch, view offset, StripDraw), +// but the destination is a caller-owned framebuffer instead of a bus window - no +// transaction, no strip transfer. This is the shared render target for scanout-buffer +// platforms (RP2350 DVI/HSTX, the desktop sim, the WASM playground): the framebuffer +// IS the composite surface, so a region is composited straight into fb in place. +// +// fb : destination, wire-order RGB565, fb_stride*fb_h pixels (caller-owned). +// fb_stride : framebuffer row stride in pixels (>= x1); rows may be wider than x1. +// items/kinds/n, background, ox/oy : the scene layer list, as picogame_render_region. +// [x0,y0,x1,y1): the framebuffer region to (re)composite - a dirty rect, or the frame. +// Bounds are clamped to [0,fb_stride) x [0,fb_h); an empty region is a no-op. +// Returns a latched BaseException raised by a StripDraw callback (the caller re-raises +// it), or MP_OBJ_NULL - the SAME contract as the strip path, minus the bus to close. +// Convert a contiguous run of `n` pixels in place from wire-order to native RGB565. +// `((v&0x00ff00ff)<<8)|((v&0xff00ff00)>>8)` byte-swaps both halfwords of a 32-bit word at once +// (GCC lowers it to a single Cortex REV16), so two pixels per word; unrolled x4 (8 px/iter) so the +// ldr/str stream pipelines on zero-wait SRAM. A run may start at an odd x (partial dirty rect), so +// peel one leading pixel to keep the word accesses 4-byte aligned (unaligned faults on M0+). +// Portable + little-endian, so it also serves the WASM native565 canvas target. +// Emulated full-screen invert for framebuffer targets (RP2350 DVI, the WASM playground) that lack a +// panel's hardware INVON/INVOFF: a flag XORed into the wire->native conversion below, so composited +// pixels come out as their negative. Toggling it (picogame_fb_set_invert) latches a one-shot "the +// whole frame must recomposite" so the flip covers the ENTIRE screen, not just the current dirty +// rects - mirroring a panel INVON, which flips everything already scanned out. The XOR folds into the +// existing REV16 byte-swap for ~free (one extra op per word). +static bool s_fb_invert = false; +static bool s_fb_invert_dirty = false; + +void picogame_fb_set_invert(bool on) { + if (on != s_fb_invert) { + s_fb_invert = on; + s_fb_invert_dirty = true; // one full-frame recomposite so the flip is global, then latch off + } +} + +bool picogame_fb_take_invert_dirty(void) { + bool d = s_fb_invert_dirty; + s_fb_invert_dirty = false; + return d; +} + +static void picogame_fb_to_native(uint16_t *px, int n) { + uint32_t inv = s_fb_invert ? 0xFFFFFFFFu : 0u; // emulated negative flash (0 = normal pass) + int i = 0; + if (n > 0 && ((uintptr_t)px & 2u) != 0) { + px[0] = (uint16_t)(__builtin_bswap16(px[0]) ^ (uint16_t)inv); + i = 1; + } + int pairs = (n - i) >> 1; + uint32_t *w = (uint32_t *)(px + i); + int p = 0; + for (; p + 4 <= pairs; p += 4) { + uint32_t v0 = w[p], v1 = w[p + 1], v2 = w[p + 2], v3 = w[p + 3]; + w[p] = (((v0 & 0x00ff00ffu) << 8) | ((v0 & 0xff00ff00u) >> 8)) ^ inv; + w[p + 1] = (((v1 & 0x00ff00ffu) << 8) | ((v1 & 0xff00ff00u) >> 8)) ^ inv; + w[p + 2] = (((v2 & 0x00ff00ffu) << 8) | ((v2 & 0xff00ff00u) >> 8)) ^ inv; + w[p + 3] = (((v3 & 0x00ff00ffu) << 8) | ((v3 & 0xff00ff00u) >> 8)) ^ inv; + } + for (; p < pairs; p++) { + uint32_t v = w[p]; + w[p] = (((v & 0x00ff00ffu) << 8) | ((v & 0xff00ff00u) >> 8)) ^ inv; + } + i += pairs << 1; + if (i < n) { + px[i] = (uint16_t)(__builtin_bswap16(px[i]) ^ (uint16_t)inv); + } +} + +// Fused wire->native byte-swap AND copy in one pass: read `n` wire pixels from `src`, write them +// NATIVE to `dst` (folding in the emulated invert XOR). Saves the separate in-place swap + memcpy +// (one whole band read+write) on the full-width publish path. REQUIRES src AND dst 4-byte aligned +// (the caller uses it only for a contiguous full-width band, where both are). +static void picogame_fb_to_native_copy(uint16_t *dst, const uint16_t *src, int n) { + uint32_t inv = s_fb_invert ? 0xFFFFFFFFu : 0u; + int pairs = n >> 1; + const uint32_t *s = (const uint32_t *)src; + uint32_t *d = (uint32_t *)dst; + int p = 0; + for (; p + 4 <= pairs; p += 4) { + uint32_t v0 = s[p], v1 = s[p + 1], v2 = s[p + 2], v3 = s[p + 3]; + d[p] = (((v0 & 0x00ff00ffu) << 8) | ((v0 & 0xff00ff00u) >> 8)) ^ inv; + d[p + 1] = (((v1 & 0x00ff00ffu) << 8) | ((v1 & 0xff00ff00u) >> 8)) ^ inv; + d[p + 2] = (((v2 & 0x00ff00ffu) << 8) | ((v2 & 0xff00ff00u) >> 8)) ^ inv; + d[p + 3] = (((v3 & 0x00ff00ffu) << 8) | ((v3 & 0xff00ff00u) >> 8)) ^ inv; + } + for (; p < pairs; p++) { + uint32_t v = s[p]; + d[p] = (((v & 0x00ff00ffu) << 8) | ((v & 0xff00ff00u) >> 8)) ^ inv; + } + if (n & 1) { + dst[n - 1] = (uint16_t)(__builtin_bswap16(src[n - 1]) ^ (uint16_t)inv); + } +} + +// Wire-order RGB565 -> RGB332 publish copy for 8-bit picodvi scanout targets (RRRGGGBB, +// the same quantization displayio's ColorConverter uses), folding in the emulated invert. +// A wire pixel w holds native v byte-swapped: hi(v) = low byte of w, lo(v) = high byte. +// R3 = top 3 of R5 = hi & 0xE0; G3 = top 3 of G6 = (hi & 0x07) << 2; +// B2 = top 2 of B5 = (lo >> 3) & 0x03 +static void picogame_fb_to_rgb332_copy(uint8_t *dst, const uint16_t *src, int n) { + uint8_t inv = s_fb_invert ? 0xFF : 0x00; + for (int i = 0; i < n; i++) { + uint16_t w = src[i]; + uint8_t hi = (uint8_t)w; // wire low byte = native high byte + uint8_t lo = (uint8_t)(w >> 8); + dst[i] = (uint8_t)((hi & 0xE0u) | ((hi & 0x07u) << 2) | ((lo >> 3) & 0x03u)) ^ inv; + } +} + +// Publish-copy context for one framebuffer region: just what fb_publish_band needs. +typedef struct { + uint16_t *fb; + int fb_stride, fmt; + int x0, region_w; + bool full_width; +} fb_bands_arg_t; + +// Publish one FINISHED band from a scratch strip into the fb (wire/native/RGB332, +// full- or partial-width). Shared by the serial loop and both cores of the split. +static void fb_publish_band(const fb_bands_arg_t *a, uint16_t *scratch, int by, int bh) { + uint16_t *fb = a->fb; + int fb_stride = a->fb_stride; + int region_w = a->region_w; + int fmt = a->fmt; + if (a->full_width) { + size_t npix = (size_t)region_w * (size_t)bh; + if (fmt == PICOGAME_FB_RGB332) { + picogame_fb_to_rgb332_copy( + (uint8_t *)fb + (size_t)by * (size_t)fb_stride, scratch, (int)npix); + } else if (fmt == PICOGAME_FB_NATIVE565) { + // Fold the wire->native byte-swap INTO the publish copy (no separate in-place swap). + picogame_fb_to_native_copy(fb + (size_t)by * (size_t)fb_stride, scratch, (int)npix); + } else { + memcpy(fb + (size_t)by * (size_t)fb_stride, scratch, npix * 2u); // wire: HW reads as-is + } + } else if (fmt == PICOGAME_FB_RGB332) { + // Partial-width 8-bit: quantize row by row straight into the byte fb (strided). + for (int r = 0; r < bh; r++) { + picogame_fb_to_rgb332_copy( + (uint8_t *)fb + (size_t)(by + r) * (size_t)fb_stride + a->x0, + scratch + (size_t)r * region_w, region_w); + } + } else { + // Partial-width: swap the scratch in place (NATIVE), then copy row by row (strided). + if (fmt == PICOGAME_FB_NATIVE565) { + picogame_fb_to_native(scratch, region_w * bh); + } + for (int r = 0; r < bh; r++) { + memcpy(fb + (size_t)(by + r) * fb_stride + a->x0, + scratch + (size_t)r * region_w, (size_t)region_w * 2u); + } + } +} + +mp_obj_t picogame_render_framebuffer( + uint16_t *fb, int fb_stride, int fb_h, int fmt, + uint16_t *scratch, int scratch_rows, + mp_obj_t *items, uint8_t *kinds, size_t n, + int x0, int y0, int x1, int y1, + uint16_t background, int ox, int oy) { + if (x0 < 0) { + x0 = 0; + } + if (y0 < 0) { + y0 = 0; + } + if (x1 > fb_stride) { + x1 = fb_stride; + } + if (y1 > fb_h) { + y1 = fb_h; + } + if (x1 <= x0 || y1 <= y0) { + return MP_OBJ_NULL; + } + int region_w = x1 - x0; + + // Tear-free NATIVE path: the framebuffer is a LIVE scanout buffer (picodvi/HDMI reads it + // continuously), so it must NEVER transiently hold wire-order pixels - a beam sampling a + // half-composed region would read byte-swapped bytes (R/B swapped -> pink). Compose+byte-swap + // into a PRIVATE scratch strip, then memcpy the finished NATIVE band into the fb; the fb only + // ever receives fully-native runs. Any residual seam is old-vs-new NATIVE content (a plain + // single-buffer tear, no colour shift) - reduced by the caller's optional vblank sync. Bands of + // scratch_rows keep the scratch small. (Wire targets / no scratch fall through to the direct + // path below, which is correct because a wire fb needs no conversion.) + if (scratch != NULL && scratch_rows > 0) { + // Full-width band: rows are contiguous in BOTH scratch and fb, so the whole band publishes in + // one pass with 4-byte-aligned pointers (fb_stride even). Partial-width rows are strided. + fb_bands_arg_t a = { + fb, fb_stride, fmt, x0, region_w, + (x0 == 0 && region_w == fb_stride) + }; + int nbands = (y1 - y0 + scratch_rows - 1) / scratch_rows; + for (int b = 0; b < nbands; b++) { + int by = y0 + b * scratch_rows; + int bh = (y1 - by) < scratch_rows ? (y1 - by) : scratch_rows; + // Compose region_w x bh OFF-SCREEN into the scratch (wire order) so the beam never sees a + // half-composited region (no sprite/HUD flicker), then publish the FINISHED band into the + // fb. Published even on a latched exception so the fb is never left half-updated. + mp_obj_t exc = picogame_blit_strip_layers( + scratch, region_w, by, bh, x0, items, kinds, n, background, ox, oy); + fb_publish_band(&a, scratch, by, bh); + if (exc != MP_OBJ_NULL) { + return exc; // StripDraw raised; caller re-raises (cleared stays false -> full repaint) + } + } + return MP_OBJ_NULL; + } + // The compositor (blitters, effects, palettes) works in wire order throughout; a NATIVE + // target is converted in place only after a region is fully composed. On a latched StripDraw + // exception the region was partially composed in wire order - convert it anyway so the fb is + // never left half wire / half native (the Scene keeps cleared=false until its render loop + // finishes, so the refresh after the exception repaints the full frame). + + // Fast path: a FULL-WIDTH region has contiguous rows, so the whole rect IS one valid strip + // buffer. Composite it in a SINGLE strip_layers call (h = the whole band) instead of one call + // per row - this amortizes all the per-row/per-layer setup (tile-blit dispatch, clip tests, + // background fill, and crucially one StripDraw Python callback per band instead of per row) + // over the band, and converts the whole contiguous region in one pass. This is the + // full-repaint / camera-scroll case (set_view -> cleared=false -> a full-screen dirty rect). + // (An RGB332 target never reaches these direct paths: its constructor always allocates + // the scratch strip, and the compositor can only write 16-bit wire pixels - composing + // in place inside a byte framebuffer would corrupt it.) + if (x0 == 0 && x1 == fb_stride) { + uint16_t *base = fb + (size_t)y0 * (size_t)fb_stride; + mp_obj_t exc = picogame_blit_strip_layers( + base, fb_stride, y0, y1 - y0, 0, items, kinds, n, background, ox, oy); + if (fmt == PICOGAME_FB_NATIVE565) { + picogame_fb_to_native(base, (y1 - y0) * fb_stride); + } + return exc; // MP_OBJ_NULL on success, else a latched StripDraw exception + } + + // Partial-width region: rows are non-contiguous, so composite (and convert) row by row. + for (int sy = y0; sy < y1; sy++) { + uint16_t *row = fb + (size_t)sy * (size_t)fb_stride + x0; + mp_obj_t exc = picogame_blit_strip_layers( + row, region_w, sy, 1, x0, items, kinds, n, background, ox, oy); + if (fmt == PICOGAME_FB_NATIVE565) { + picogame_fb_to_native(row, region_w); + } + if (exc != MP_OBJ_NULL) { + return exc; // StripDraw raised a BaseException; caller re-raises (no bus open) + } + } + return MP_OBJ_NULL; +} +#endif // CIRCUITPY_PICOGAME_FRAMEBUFFER diff --git a/shared-module/picogame/__init__.h b/shared-module/picogame/__init__.h new file mode 100644 index 00000000000..a4377292219 --- /dev/null +++ b/shared-module/picogame/__init__.h @@ -0,0 +1,308 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include "py/obj.h" +#include "shared-bindings/busdisplay/BusDisplay.h" +#include "shared-module/picogame/Bitmap.h" +#include "shared-module/picogame/Sprite.h" + +// Hardware-FPU boards run the pseudo-3D/math primitives (picogame.project) on plain float32 - +// faster than the soft-float-equivalent fixed path and free of 16.16 range limits; no-FPU +// targets (Cortex-M0+) use integer 16.16. Only ONE path is compiled per board. The default +// follows the architecture; a board can override with CIRCUITPY_PICOGAME_FPU=0/1 in its +// mpconfigboard.mk. Python reads `picogame.FPU` to pack camera/point buffers to match. +#ifndef CIRCUITPY_PICOGAME_FPU +#if (defined(__ARM_FP) && (__ARM_FP != 0)) || (defined(__riscv_flen) && (__riscv_flen > 0)) +#define CIRCUITPY_PICOGAME_FPU (1) +#else +#define CIRCUITPY_PICOGAME_FPU (0) +#endif +#endif + +// Sample one texel as wire RGB565; false = transparent (skip). Shared by the sprite/canvas +// blit paths so they inline one copy (see the blit contract: PAL8 indices must be < palette len). +static inline bool src_pixel_s(int format, const uint8_t *data, const uint16_t *pal, + bool transp, uint16_t key, int idx, uint16_t *out) { + if (format == PICOGAME_FMT_PAL8) { + uint8_t i = data[idx]; + if (transp && i == (uint8_t)key) { + return false; + } + *out = pal[i]; // indices must be < palette length (see blit contract) + return true; + } + // GC buffer is >=4-byte aligned; silence xtensa -Wcast-align (see picogame_blit_bitmap). + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wcast-align" + uint16_t v = ((const uint16_t *)data)[idx]; + #pragma GCC diagnostic pop + if (transp && v == key) { + return false; + } + *out = v; + return true; +} + + +// Scene layer kinds (tags stored alongside items so blit/dirty can dispatch +// without cross-referencing shared-bindings type objects). +enum { + PICOGAME_KIND_SPRITE = 0, + PICOGAME_KIND_TILEMAP = 1, + PICOGAME_KIND_PARTICLES = 2, + PICOGAME_KIND_CANVAS = 3, + PICOGAME_KIND_STRIPDRAW = 4, + PICOGAME_KIND_TRIANGLES = 5, + // High bit on a kind = "fixed": the item ignores the scene view offset + // (camera), so HUD / score / dialog stay put while the world scrolls. + PICOGAME_KIND_FIXED = 0x80, + PICOGAME_KIND_MASK = 0x7f, +}; + +// StripDraw: immediate-mode layer. Holds NO pixel buffer - instead its `callback` +// is invoked once per render strip with a Canvas "view" repointed at the live strip +// buffer, so the user draws primitives straight into the strip (zero RAM vs a Canvas, +// which costs w*h*2 bytes). Its rect is repainted every frame (it's for animated / +// scanline content: pseudo-3D, gradients, procedural backgrounds). The view's local +// (0,0) maps to screen (vx, vy) handed to the callback. `faulted` latches after the +// callback raises once, so a buggy callback prints one traceback, not one per strip. +typedef struct { + mp_obj_base_t base; + mp_obj_t callback; // draw(view, vx, vy, vw, vh): vx/vy = screen origin of view (0,0) + mp_obj_t view; // a reused picogame_canvas_obj_t (data repointed each strip) + int32_t x, y, w, h; // scene rect (int32: scene coords, big-world safe) + int32_t dx1, dy1, dx2, dy2; // accumulated dirty rect (scene coords) when !always_dirty - the same + // picogame_dirty_* accumulator Canvas/Tilemap use, so invalidate() can mark + // a sub-rect and the Scene repaints only that region (not the whole layer). + bool faulted; + bool always_dirty; // True: repaint every frame (animated). False: only the dirty rect (on-change UI). +} picogame_stripdraw_obj_t; + +// Triangles: a retained SCREEN-SPACE triangle batch the compositor rasterises entirely +// in C (per strip, band-rejected) - no Python callback per strip, so unlike StripDraw it +// stays composable without re-entering Python mid-frame. verts (int16 x0,y0,x1,y1,x2,y2 per tri) and +// colors (uint16 wire RGB565 per tri) are CALLER-OWNED arrays (refs held for GC; fill +// them in place). Setting `count` selects how many draw and marks the layer dirty +// full-screen (a 3D frame repaints everything anyway). +typedef struct { + mp_obj_base_t base; + mp_obj_t verts_obj, colors_obj; // GC anchors for the caller's arrays + const int16_t *verts; + const uint16_t *colors; + uint16_t count, cap; // cap = what the buffers can hold + int32_t dx1, dy1, dx2, dy2; // dirty accumulator (count-set -> full screen) +} picogame_triangles_obj_t; + +static inline int picogame_imin(int a, int b) { + return a < b ? a : b; +} +static inline int picogame_imax(int a, int b) { + return a > b ? a : b; +} + +// Drawn top-left in scene pixels: the logical position minus the anchor offset +// (anchor is a 1/256 fraction of the bitmap size). Used by BOTH the blitter and +// the dirty-rect tracker so they always agree on where the sprite lands. +static inline void picogame_sprite_topleft(const picogame_sprite_obj_t *s, int *tx, int *ty) { + int w = (s->bitmap != NULL) ? s->bitmap->width : 0; + int h = (s->bitmap != NULL) ? s->bitmap->height : 0; + int sw = (w * s->scale) >> 8; // anchor is a fraction of the SCALED size + int sh = (h * s->scale) >> 8; + // The blitter only honours transpose on the fast path (scale==256); the scaled blitter ignores it. + // Swap the footprint ONLY when scale==256, or aabb/topleft disagree with what's drawn (trailing). + if ((s->flags & PICOGAME_SPR_TRANSPOSE) && s->scale == 256) { // 90deg transpose swaps footprint + int t = sw; // picogame_sprite_aabb, or the blit top-left and the + sw = sh; // tracked dirty rect disagree (sprite trails) + sh = t; + } + *tx = (s->x >> 8) - ((int)s->anchor_x * sw >> 8); + *ty = (s->y >> 8) - ((int)s->anchor_y * sh >> 8); +} + +// Drawn screen-space bounding box of a sprite (accounts for scale + rotation). +// Used by the dirty-rect tracker so it always covers the transformed sprite. +void picogame_sprite_aabb(const picogame_sprite_obj_t *s, int *x1, int *y1, int *x2, int *y2); + +// Per-pixel blit effect, shared by all three blit paths. One mode at a time; a NULL +// pointer means "no effect" (the fast path). SHADOW darkens the destination, FLASH +// replaces opaque pixels with `color`, DITHER skips pixels via a Bayer pattern (0..16 +// transparency) for fake translucency without alpha. +enum { PICOGAME_FX_NONE = 0, PICOGAME_FX_SHADOW, PICOGAME_FX_FLASH, PICOGAME_FX_DITHER, PICOGAME_FX_TINT }; +typedef struct { + uint8_t mode; + uint16_t color; // FLASH: solid colour to write; TINT: colour to multiply by (wire RGB565) + uint8_t level; // DITHER: 0..16 transparency (higher = more pixels skipped) +} picogame_fx_t; + +// Shared dirty-rect accumulator over a contiguous int32 [x1,y1,x2,y2] (Canvas + Tilemap both end in +// dx1,dy1,dx2,dy2). INT32 sentinels so big-world (>32767 px) scene coords still accumulate. +void picogame_dirty_reset(int32_t *r); +void picogame_dirty_union(int32_t *r, int x1, int y1, int x2, int y2); +bool picogame_dirty_take(int32_t *r, int *x1, int *y1, int *x2, int *y2); + +// Blit one frame of a bitmap at screen (dx0, dy0) into the strip buffer that +// covers [ox, ox+bw) x [oy, oy+bh). Shared by sprites and tilemap tiles. +// fxm: per-pixel effect (NULL = plain colour copy). +void picogame_blit_bitmap( + uint16_t *buf, int bw, int bh, int ox, int oy, + picogame_bitmap_obj_t *bm, int dx0, int dy0, int frame, bool flip_x, bool flip_y, + bool transpose, const picogame_fx_t *fxm); + +// Nearest-neighbour scaled blit (axis-aligned); scale is 8.8 fixed-point. +void picogame_blit_bitmap_scaled( + uint16_t *buf, int bw, int bh, int ox, int oy, + picogame_bitmap_obj_t *bm, int dx0, int dy0, int frame, bool flip_x, bool flip_y, + uint16_t scale, const picogame_fx_t *fxm); + +// Full affine blit (scale + rotation about the anchor); (px,py)=screen anchor point, +// (pivx,pivy)=that anchor in SOURCE pixels. Nearest-neighbour inverse map. The transform is +// PRECOMPUTED by the caller (the sprite's xf_* cache): minx..maxy = the screen-space corner +// bbox, ic/is = the 16.16 inverse-map steps - so a per-strip call does no trig/divides. +void picogame_blit_bitmap_affine( + uint16_t *buf, int bw, int bh, int ox, int oy, + picogame_bitmap_obj_t *bm, int px, int py, int pivx, int pivy, + int frame, bool flip_x, bool flip_y, + int minx, int miny, int maxx, int maxy, int32_t ic, int32_t is, + const picogame_fx_t *fxm); + +// ===== OUTPUT TRANSPORT SEAM ===================================================== +// picogame's compositor (picogame_blit_strip_layers) is OUTPUT-AGNOSTIC: it composites +// scene layers into a plain wire-order RGB565 strip buffer, knowing nothing about the +// destination. A physical display is reached only through the small transport contract +// below, so a non-CircuitPython port (e.g. a MicroPython framebuf/SPI backend) can reuse +// the whole compositor AND the generic picogame_render_region orchestrator and reimplement +// ONLY these few functions. `picogame_output_t` is the opaque display handle they take. +// (A RAM-framebuffer destination is a separate backend: picogame_render_framebuffer.) +// +// Strip-path contract a backend provides: +// picogame_strip_begin - open a window for [x0,y0,x1,y1); return strip geometry +// picogame_out_strip_send - push one composited strip (region_w*sh px, wire RGB565) +// picogame_out_strip_end - close the transaction +// picogame_set_invert - panel hardware colour inversion (a free full-screen flash) +// picogame_set_pixel_format - panel COLMOD (RGB565/RGB444), when CIRCUITPY_PICOGAME_RGB444 +// +// CircuitPython backend: picogame_output_t == busdisplay; the impl lives in __init__.c. +typedef busdisplay_busdisplay_obj_t picogame_output_t; + +// Fill a strip with background, then composite items (sprites and tilemaps) in +// order (items[0] = bottom). kinds[i] selects the type; kinds == NULL means +// every item is a sprite. (ox, oy) is the view offset added to item positions +// (scene space -> screen space) for camera/centering. +// Returns a latched BaseException (Ctrl-C / ReloadException) raised by a StripDraw callback, or +// MP_OBJ_NULL. The caller must re-raise it AFTER closing the display transaction. +#if defined(PICOGAME_HAS_INTERP) +// rp2-port SIO-interpolator mode7 row walker. Fast path only: +// PAL8, opaque, stride == tw, log2(tw)+log2(th) <= 16; the caller guards and falls back. +void picogame_mode7_row_interp(uint16_t *dst, int n, + const uint8_t *tex, const uint16_t *pal, + uint32_t fx, uint32_t fy, int32_t stepx, int32_t stepy, + int shx, int shy, int ltw, int lth); +#endif + +// Racing-road curve pass (see the implementation comment in __init__.c). +void picogame_road_edges(int16_t *rl, int16_t *rr, const int32_t *hw_q16, int n, + int32_t cx_q16, int32_t dist, const int32_t *cfg); + +mp_obj_t picogame_blit_strip_layers( + uint16_t *buf, int region_w, int strip_top, int strip_h, int x0, + mp_obj_t *items, uint8_t *kinds, size_t n, uint16_t background, int ox, int oy); + +// Compute strip geometry and open a render window on the display (set region, +// begin transaction, send RAMWR). Returns false if the region is empty; raises +// if the buffer is too small for the region width. Fills *region_w and *strip_h. +bool picogame_strip_begin( + picogame_output_t *display, + int *x0, int *y0, int *x1, int *y1, size_t buffer_pixels, + int *region_w, int *strip_h); // clamps *x0..*y1 to the panel in place (caller loops on them) + +// Portable backend: strip-render a layered scene region to ANY busdisplay via +// its (blocking) bus.send - single buffer, no DMA. Same layer dispatch as the +// fast path (kinds + view offset), so it is the cross-port fallback for Scene on +// targets without the platform DMA Display. `kinds == NULL` => all sprites. +void picogame_render_region( + picogame_output_t *display, + mp_obj_t *items, uint8_t *kinds, size_t n, + uint16_t *buffer, size_t buffer_pixels, + int16_t x0, int16_t y0, int16_t x1, int16_t y1, + uint16_t background, int ox, int oy); + +// Toggle the panel's hardware colour inversion (INVON/INVOFF) - a free full-screen flash. +void picogame_set_invert(picogame_output_t *display, bool on); + +#if CIRCUITPY_PICOGAME_FRAMEBUFFER +// Emulated invert for a picogame.Framebuffer target (no hardware INVON): set the flag (XORed into the +// wire->native conversion) and, via take_invert_dirty(), force one whole-frame recomposite on toggle. +void picogame_fb_set_invert(bool on); +bool picogame_fb_take_invert_dirty(void); +#endif + +#if CIRCUITPY_PICOGAME_RGB444 // compiled in only on boards that opt into RGB444 (default off) +// Set panel pixel format (COLMOD): rgb444 -> 12-bit RGB444, else 16-bit RGB565. +void picogame_set_pixel_format(picogame_output_t *display, bool rgb444); + +// Pack `npix` (even) wire-order RGB565 pixels in `buf` IN-PLACE to 12-bit RGB444; returns bytes. +size_t picogame_pack_rgb444(uint16_t *buf, size_t npix); +#endif + +// Universal sprite-only convenience wrapper over picogame_render_region. +void picogame_render( + picogame_output_t *display, + mp_obj_t *items, size_t n, + uint16_t *buffer, size_t buffer_pixels, + int16_t x0, int16_t y0, int16_t x1, int16_t y1, + uint16_t background); + +#if CIRCUITPY_PICOGAME_FRAMEBUFFER +// Rows per compose band for the tear-free NATIVE framebuffer path: composite+byte-swap into a +// private scratch strip, then memcpy the finished NATIVE band into the live scanout buffer - so a +// picodvi/HDMI beam scanning the framebuffer never samples a half-composed WIRE-order region (which +// would read as byte-swapped / pink). Small band = small scratch (width*this*2 bytes). +#define PICOGAME_FB_SCRATCH_H 16 +// A render TARGET that is a caller-owned RAM framebuffer (wire-order RGB565), used in +// place of a BusDisplay for scanout-buffer platforms: the WASM playground (heap +// buffer read out to a canvas), the desktop sim, and FruitJam (the DVI/HSTX scanout +// buffer). Holds a WriteableBuffer alive + a typed view of it; allocation is the +// caller's (a bytearray in WASM, the DVI buffer memoryview on FruitJam), so the engine +// stays platform-neutral. Scene / render can target this instead of a display. +// Output pixel format of a picogame.Framebuffer target. The compositor always works in +// wire-order RGB565; the format only selects the publish conversion. +enum { + PICOGAME_FB_WIRE565 = 0, // no conversion (WASM playground / sim readout) + PICOGAME_FB_NATIVE565 = 1, // byte-swap to native RGB565 (picodvi 16-bit scanout) + PICOGAME_FB_RGB332 = 2, // quantize to RGB332 bytes (picodvi 8-bit scanout, e.g. + // Fruit Jam 640x480 - its max resolution is 8bpp-only) +}; + +typedef struct { + mp_obj_base_t base; + mp_obj_t buffer; // the backing WriteableBuffer (kept alive) + uint16_t *fb; // typed view of buffer.buf: width*height px - RGB565 (2 B/px) for the + // 565 formats; cast to uint8_t* per-pixel bytes for PICOGAME_FB_RGB332 + int width; + int height; + uint8_t fmt; // PICOGAME_FB_* output format (see enum above) + mp_obj_t scratch_buf; // GC-kept bytearray backing `scratch`; mp_const_none if none + uint16_t *scratch; // private compose strip: width*scratch_rows px, or NULL + int scratch_rows; // rows in `scratch` (PICOGAME_FB_SCRATCH_H), 0 if none +} picogame_framebuffer_obj_t; + +// Full-frame RAM-framebuffer backend: same layered compositor as the SPI strip path +// but composited straight into a caller-owned wire-order RGB565 framebuffer (no bus). +// The shared render target for scanout-buffer platforms (RP2350 DVI/HSTX, sim, WASM). +// [x0,y0,x1,y1) is the region to (re)composite (clamped to the framebuffer). Returns a +// latched StripDraw BaseException for the caller to re-raise, or MP_OBJ_NULL. +mp_obj_t picogame_render_framebuffer( + uint16_t *fb, int fb_stride, int fb_h, int fmt, + uint16_t *scratch, int scratch_rows, + mp_obj_t *items, uint8_t *kinds, size_t n, + int x0, int y0, int x1, int y1, + uint16_t background, int ox, int oy); +#endif // CIRCUITPY_PICOGAME_FRAMEBUFFER diff --git a/shared-module/picogame/pg_compat.h b/shared-module/picogame/pg_compat.h new file mode 100644 index 00000000000..92c4168f6d8 --- /dev/null +++ b/shared-module/picogame/pg_compat.h @@ -0,0 +1,64 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Vladimir Smitka +// +// SPDX-License-Identifier: MIT +// +// pg_compat.h — the small CircuitPython core-API delta the picogame engine relies on, +// OWNED BY THE ENGINE (not injected by a build-level force-`-include`). On CircuitPython +// this is a pure pass-through (every symbol is native); on a bare MicroPython build it +// supplies the ~9-symbol delta CP added over MicroPython. This lets the SAME engine C +// compile on both without a per-build shim impersonating CircuitPython. +// +// Engine TUs `#include "shared-module/picogame/pg_compat.h"` where they use any of these +// (it replaces the former `#include "py/objproperty.h"` in the bindings TUs). The function +// bodies for the MicroPython branch live in pg_compat_mp.c (compiled ONLY on MicroPython). +#pragma once + +#include "py/runtime.h" + +#if defined(CIRCUITPY) +// CircuitPython: everything below is native — pure pass-through. +#include "py/objproperty.h" +#else +// MicroPython: the core-API delta the engine needs that bare MicroPython lacks. +#include "py/obj.h" + +#ifndef RUN_BACKGROUND_TASKS +#define RUN_BACKGROUND_TASKS (mp_handle_pending(MP_HANDLE_PENDING_CALLBACKS_AND_EXCEPTIONS)) +#endif +#ifndef m_malloc_without_collect +#define m_malloc_without_collect(n) m_malloc(n) +#endif +#ifndef mp_raise_RuntimeError +#define mp_raise_RuntimeError(msg) mp_raise_msg(&mp_type_RuntimeError, (msg)) +#endif + +mp_int_t mp_arg_validate_int_min(mp_int_t i, mp_int_t min, qstr arg_name); +mp_int_t mp_arg_validate_int_range(mp_int_t i, mp_int_t min, mp_int_t max, qstr arg_name); +mp_obj_t mp_arg_validate_type(mp_obj_t obj, const mp_obj_type_t *type, qstr arg_name); +NORETURN void mp_raise_ValueError_varg(mp_rom_error_text_t fmt, ...); +NORETURN void mp_raise_TypeError_varg(mp_rom_error_text_t fmt, ...); +mp_obj_t mp_obj_new_bytearray_of_zeros(size_t n); + +// CircuitPython's MP_PROPERTY_GETTER / MP_PROPERTY_GETSET convenience macros. Bare +// MicroPython keeps the property object private to py/objproperty.c; redeclare the +// struct with MP's EXACT layout + the macros in the non-native form (identical to +// CircuitPython's own !MICROPY_PY_OBJ_PROPERTY_NATIVE branch), so the generated +// property objects are ABI-compatible with MP's mp_type_property. +#if MICROPY_PY_BUILTINS_PROPERTY +typedef struct _mp_obj_property_t { + mp_obj_base_t base; + mp_obj_t proxy[3]; // getter, setter, deleter +} mp_obj_property_getset_t; +#ifndef MP_PROPERTY_GETTER +#define MP_PROPERTY_GETTER(P, G) \ + const mp_obj_property_getset_t P = { .base.type = &mp_type_property, .proxy = {G, MP_ROM_NONE, MP_ROM_NONE} } +#endif +#ifndef MP_PROPERTY_GETSET +#define MP_PROPERTY_GETSET(P, G, S) \ + const mp_obj_property_getset_t P = { .base.type = &mp_type_property, .proxy = {G, S, MP_ROM_NONE} } +#endif +#endif // MICROPY_PY_BUILTINS_PROPERTY + +#endif // CIRCUITPY diff --git a/shared-module/sdcardio/SDCard.c b/shared-module/sdcardio/SDCard.c index 40d09aa99ca..5c9050f2662 100644 --- a/shared-module/sdcardio/SDCard.c +++ b/shared-module/sdcardio/SDCard.c @@ -318,7 +318,7 @@ static mp_rom_error_text_t init_card(sdcardio_sdcard_obj_t *self) { } if (csd_version == 1) { - self->sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024; + self->sectors = (((csd[7] & 0x3F) << 16 | csd[8] << 8 | csd[9]) + 1) * 1024; } else { uint32_t block_length = 1 << (csd[5] & 0xF); uint32_t c_size = ((csd[6] & 0x3) << 10) | (csd[7] << 2) | ((csd[8] & 0xC) >> 6); diff --git a/shared-module/ssl/SSLSocket.c b/shared-module/ssl/SSLSocket.c index 0c374da771f..a9969505509 100644 --- a/shared-module/ssl/SSLSocket.c +++ b/shared-module/ssl/SSLSocket.c @@ -13,7 +13,7 @@ #include "shared/netutils/netutils.h" #include "py/mperrno.h" #include "py/mphal.h" -#include "py/objstr.h" +#include "py/objarray.h" #include "py/runtime.h" #include "py/stream.h" #include "supervisor/shared/tick.h" @@ -40,7 +40,9 @@ static void mbedtls_debug(void *ctx, int level, const char *file, int line, cons #define DEBUG_PRINT(...) do {} while (0) #endif -static MP_NORETURN void mbedtls_raise_error(int err) { +// Raise an OSError for an mbedtls error code. +// `flags` is a bitmask from mbedtls_ssl_get_verify_result(), or 0 if not a verify error. +static MP_NORETURN void mbedtls_raise_error_flags(int err, uint32_t flags) { // _mbedtls_ssl_send and _mbedtls_ssl_recv (below) turn positive error codes from the // underlying socket into negative codes to pass them through mbedtls. Here we turn them // positive again so they get interpreted as the OSError they really are. The @@ -53,37 +55,78 @@ static MP_NORETURN void mbedtls_raise_error(int err) { mp_raise_OSError(MP_EWOULDBLOCK); } + // All ones means mbedtls says it has nothing to report: it set to all ones + // when a verify callback fails, and mbedtls_ssl_get_verify_result() returns + // this when there is no session at all. + if (flags == UINT32_MAX) { + flags = 0; + } + #if defined(MBEDTLS_ERROR_C) // Including mbedtls_strerror takes about 1.5KB due to the error strings. // MBEDTLS_ERROR_C is the define used by mbedtls to conditionally include mbedtls_strerror. // It is set/unset in the MBEDTLS_CONFIG_FILE which is defined in the Makefile. - // Try to allocate memory for the message - #define ERR_STR_MAX 80 // mbedtls_strerror truncates if it doesn't fit - mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t); - byte *o_str_buf = m_malloc_without_collect(ERR_STR_MAX); - if (o_str == NULL || o_str_buf == NULL) { + // Large enough for the longest mbedtls_strerror() output, which is about 100 + // characters when it joins a high-level and a low-level name with '+', and for + // the longest certificate verification failure name, which is 81 characters. + #define ERR_STR_MAX 128 // mbedtls_strerror truncates if it doesn't fit + // One byte larger than the bound passed to mbedtls_strerror(), so that the + // strncpy() inside it has a bound smaller than the size of buf. Truncation + // there is safe and deliberate, but an equal bound trips -Wstringop-truncation. + char buf[ERR_STR_MAX + 1]; + + // Assemble the error message in a vstr. + // If we run out of heap, catch the MemoryError and fall back to just the error number. + mp_obj_t exc; + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + vstr_t vstr; + vstr_init(&vstr, ERR_STR_MAX); + + mbedtls_strerror(err, buf, ERR_STR_MAX); + vstr_add_str(&vstr, buf); + + #if !defined(MBEDTLS_X509_REMOVE_INFO) + // Call mbedtls_x509_crt_verify_info() to get the error string + // for each individual verify error bit. + // This allows for easier string management. + // Several verify errorbits are often set at once: + // mbedtls or's together the error flags of every certificate in the chain. + // For instance, a cert served under the wrong name and signed by an + // unknown CA reports CN_MISMATCH and NOT_TRUSTED together. + for (uint32_t bit = 1; bit != 0; bit <<= 1) { + if ((flags & bit) == 0) { + continue; + } + // The prefix is added to the beginning of the message. + // We drop the supplied trailing newline. + int info_len = mbedtls_x509_crt_verify_info(buf, ERR_STR_MAX, "; ", bit); + if (info_len > 1) { + // -1 to drop the newline. + vstr_add_strn(&vstr, buf, info_len - 1); + } + } + #endif + + mp_obj_t args[2] = { MP_OBJ_NEW_SMALL_INT(err), mp_obj_new_str_from_utf8_vstr(&vstr) }; + exc = mp_obj_exception_make_new(&mp_type_OSError, 2, 0, args); + nlr_pop(); + } else { + // Could not build the message, so report the number by itself. mp_raise_OSError(err); } - - // print the error message into the allocated buffer - mbedtls_strerror(err, (char *)o_str_buf, ERR_STR_MAX); - size_t len = strlen((char *)o_str_buf); - - // Put the exception object together - o_str->base.type = &mp_type_str; - o_str->data = o_str_buf; - o_str->len = len; - o_str->hash = qstr_compute_hash(o_str->data, o_str->len); - // raise - mp_obj_t args[2] = { MP_OBJ_NEW_SMALL_INT(err), MP_OBJ_FROM_PTR(o_str)}; - nlr_raise(mp_obj_exception_make_new(&mp_type_OSError, 2, 0, args)); + nlr_raise(exc); #else - // mbedtls is compiled without error strings so we simply return the err number + // mbedtls is compiled without error strings, so just return the err number mp_raise_OSError(err); // err is typically a large negative number #endif } +static MP_NORETURN void mbedtls_raise_error(int err) { + mbedtls_raise_error_flags(err, 0); +} + // Because ssl_socket_send and ssl_socket_recv_into are callbacks from mbedtls code, // it is not OK to exit them by raising an exception (nlr_jump'ing through // foreign code is not permitted). Instead, preserve the error number of any OSError @@ -372,6 +415,15 @@ static void do_handshake(ssl_sslsocket_obj_t *self) { cleanup: self->closed = true; + + // Verification flags are only valid for CERT_VERIFY_FAILED. + // Read them before mbedtls_ssl_free() below: they live in the ssl context's + // session_negotiate and are gone once it is freed. + uint32_t verify_flags = 0; + if (ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) { + verify_flags = mbedtls_ssl_get_verify_result(&self->ssl); + } + mbedtls_pk_free(&self->pkey); mbedtls_x509_crt_free(&self->cert); mbedtls_x509_crt_free(&self->cacert); @@ -385,7 +437,7 @@ static void do_handshake(ssl_sslsocket_obj_t *self) { } else if (ret == MBEDTLS_ERR_X509_BAD_INPUT_DATA) { mp_raise_ValueError(MP_ERROR_TEXT("invalid cert")); } else { - mbedtls_raise_error(ret); + mbedtls_raise_error_flags(ret, verify_flags); } } diff --git a/shared-module/synthio/Biquad.c b/shared-module/synthio/Biquad.c index 08aab81a7bf..edefd071403 100644 --- a/shared-module/synthio/Biquad.c +++ b/shared-module/synthio/Biquad.c @@ -197,6 +197,10 @@ void synthio_biquad_filter_reset(biquad_filter_state *st) { memset(&st->x, 0, 4 * sizeof(int16_t)); } +static inline int32_t biquad_filter_sample(int32_t input, int32_t a1, int32_t a2, int32_t b0, int32_t b1, int32_t b2, int32_t x0, int32_t x1, int32_t y0, int32_t y1) { + return synthio_sat16((b0 * input + b1 * x0 + b2 * x1 - a1 * y0 - a2 * y1 + (1 << (BIQUAD_SHIFT - 1))), BIQUAD_SHIFT); +} + void synthio_biquad_filter_samples(mp_obj_t self_in, biquad_filter_state *st, int32_t *buffer, size_t n_samples) { synthio_biquad_t *self = MP_OBJ_TO_PTR(self_in); @@ -213,7 +217,7 @@ void synthio_biquad_filter_samples(mp_obj_t self_in, biquad_filter_state *st, in for (size_t n = n_samples; n; --n, ++buffer) { int32_t input = *buffer; - int32_t output = synthio_sat16((b0 * input + b1 * x0 + b2 * x1 - a1 * y0 - a2 * y1 + (1 << (BIQUAD_SHIFT - 1))), BIQUAD_SHIFT); + int32_t output = biquad_filter_sample(input, a1, a2, b0, b1, b2, x0, x1, y0, y1); x1 = x0; x0 = input; @@ -226,3 +230,16 @@ void synthio_biquad_filter_samples(mp_obj_t self_in, biquad_filter_state *st, in st->y[0] = y0; st->y[1] = y1; } + +int32_t synthio_biquad_filter_sample(mp_obj_t self_in, biquad_filter_state *st, int32_t input) { + synthio_biquad_t *self = MP_OBJ_TO_PTR(self_in); + + int32_t output = biquad_filter_sample(input, self->a1, self->a2, self->b0, self->b1, self->b2, st->x[0], st->x[1], st->y[0], st->y[1]); + + st->x[1] = st->x[0]; + st->x[0] = input; + st->y[1] = st->y[0]; + st->y[0] = output; + + return output; +} diff --git a/shared-module/synthio/Biquad.h b/shared-module/synthio/Biquad.h index 3b9920ed92f..cc9b4f53b0e 100644 --- a/shared-module/synthio/Biquad.h +++ b/shared-module/synthio/Biquad.h @@ -27,3 +27,4 @@ typedef struct { void common_hal_synthio_biquad_tick(mp_obj_t self_in); void synthio_biquad_filter_reset(biquad_filter_state *st); void synthio_biquad_filter_samples(mp_obj_t self_in, biquad_filter_state *st, int32_t *buffer, size_t n_samples); +int32_t synthio_biquad_filter_sample(mp_obj_t self_in, biquad_filter_state *st, int32_t input); diff --git a/shared-module/synthio/Note.c b/shared-module/synthio/Note.c index c8c307bb415..01c7e4ba487 100644 --- a/shared-module/synthio/Note.c +++ b/shared-module/synthio/Note.c @@ -196,13 +196,11 @@ static uint32_t pitch_bend(uint32_t frequency_scaled, int32_t bend_value) { uint32_t synthio_note_step(synthio_note_obj_t *self, int32_t sample_rate, int16_t dur, int16_t loudness[2]) { int panning = synthio_block_slot_get_scaled(&self->panning, -ALMOST_ONE, ALMOST_ONE); - int left_panning_scaled, right_panning_scaled; + int left_panning_scaled = 32768, right_panning_scaled = 32768; if (panning >= 0) { - left_panning_scaled = 32768; - right_panning_scaled = 32767 - panning; + left_panning_scaled = 32767 - panning; } else { - right_panning_scaled = 32768; - left_panning_scaled = 32767 + panning; + right_panning_scaled = 32767 + panning; } int amplitude = synthio_block_slot_get_scaled(&self->amplitude, -ALMOST_ONE, ALMOST_ONE); diff --git a/shared-module/synthio/__init__.c b/shared-module/synthio/__init__.c index db0c449bfab..b30b5f35b5f 100644 --- a/shared-module/synthio/__init__.c +++ b/shared-module/synthio/__init__.c @@ -289,17 +289,54 @@ static mp_obj_t synthio_synth_get_note_filter(mp_obj_t note_obj) { return mp_const_none; } -static void sum_with_loudness(int32_t *out_buffer32, int32_t *tmp_buffer32, int16_t loudness[2], size_t dur, int synth_chan) { +// Rather than immediately changing the loudness of audio playback, we keep a separate buffer of +// the "active" loudness and wait until we meet the conditions of a "zero crossing". A zero crossing +// occurs when either the current value is 0 or the value changes from negative to positive or +// vice-versa. This is detected by keeping a copy of the previous frame of audio data and checking +// to see if the sign of the value has changed. By only changing loudness during zero crossings, we +// avoid audible pops/clicks which can be unpleasant. +static void assign_loudness(int32_t word, int32_t *last_word, int16_t active_loudness[2], int16_t pending_loudness[2]) { + // If the active loudness already matches the pending loudness, exit early. + if (MP_LIKELY(active_loudness[0] == pending_loudness[0]) && MP_LIKELY(active_loudness[1] == pending_loudness[1])) { + return; + } + + // Check for a zero crossing: current value is 0 or has changed sign from the previous value. + if (word == 0 || (*last_word != 0 && ((*last_word > 0) == (word < 0) || (*last_word < 0) == (word > 0)))) { + // Copy over our pending loudness. Will cause future calls to `assign_loudness` to exit + // early. + active_loudness[0] = pending_loudness[0]; + active_loudness[1] = pending_loudness[1]; + } else { + // Update our copy of the previous word for future comparisons (an initial value of 0 is + // ignored). + *last_word = word; + } +} + +static void sum_with_loudness(int32_t *out_buffer32, int32_t *tmp_buffer32, int16_t active_loudness[2], int16_t pending_loudness[2], size_t dur, int synth_chan) { + int32_t word, last_word = 0; if (synth_chan == 1) { for (size_t i = 0; i < dur; i++) { - *out_buffer32++ += synthio_sat16((*tmp_buffer32++ *loudness[0]), 16); + word = *tmp_buffer32++; + assign_loudness(word, &last_word, active_loudness, pending_loudness); + *out_buffer32++ += synthio_sat16((word * active_loudness[0]), 16); } } else { for (size_t i = 0; i < dur; i++) { - *out_buffer32++ += synthio_sat16((*tmp_buffer32 * loudness[0]), 16); - *out_buffer32++ += synthio_sat16((*tmp_buffer32++ *loudness[1]), 16); + word = *tmp_buffer32; + assign_loudness(word, &last_word, active_loudness, pending_loudness); + *out_buffer32++ += synthio_sat16((word * active_loudness[0]), 16); + *out_buffer32++ += synthio_sat16((word * active_loudness[1]), 16); + tmp_buffer32++; } } + + // Force the active loudness to match the pending loudness just in case the conditions of a + // zero crossing weren't met within the last `SYNTHIO_MAX_DUR` frames. Will ensure minimal + // delay in amplitude or panning changes at the potential expensive of an audible pop. + active_loudness[0] = pending_loudness[0]; + active_loudness[1] = pending_loudness[1]; } void synthio_synth_synthesize(synthio_synth_t *synth, uint8_t **bufptr, uint32_t *buffer_length, uint8_t channel) { @@ -351,7 +388,7 @@ void synthio_synth_synthesize(synthio_synth_t *synth, uint8_t **bufptr, uint32_t } // adjust loudness by envelope - sum_with_loudness(out_buffer32, tmp_buffer32, loudness, dur, synth->base.channel_count); + sum_with_loudness(out_buffer32, tmp_buffer32, synth->active_loudness[chan], loudness, dur, synth->base.channel_count); } int16_t *out_buffer16 = (int16_t *)(void *)synth->buffers[synth->buffer_index]; diff --git a/shared-module/synthio/__init__.h b/shared-module/synthio/__init__.h index 5162b49fd55..0335831f80f 100644 --- a/shared-module/synthio/__init__.h +++ b/shared-module/synthio/__init__.h @@ -55,6 +55,7 @@ typedef struct synthio_synth { uint32_t accum[CIRCUITPY_SYNTHIO_MAX_CHANNELS]; uint32_t ring_accum[CIRCUITPY_SYNTHIO_MAX_CHANNELS]; synthio_envelope_state_t envelope_state[CIRCUITPY_SYNTHIO_MAX_CHANNELS]; + int16_t active_loudness[CIRCUITPY_SYNTHIO_MAX_CHANNELS][2]; } synthio_synth_t; typedef struct { diff --git a/shared-module/usb_audio/USBMicrophone.c b/shared-module/usb_audio/USBMicrophone.c index fd5309abeb9..26a185ff0e9 100644 --- a/shared-module/usb_audio/USBMicrophone.c +++ b/shared-module/usb_audio/USBMicrophone.c @@ -11,6 +11,7 @@ #include "shared-bindings/usb_audio/USBMicrophone.h" #include "shared-module/usb_audio/__init__.h" +#include "shared-module/usb_audio/usb_audio_descriptors.h" #include "shared-module/audiocore/__init__.h" // Only one microphone may feed the single USB IN endpoint at a time. This points @@ -53,7 +54,7 @@ void common_hal_usb_audio_usbmicrophone_play(usb_audio_usbmicrophone_obj_t *self if (audiosample_get_channel_count(sample_base) != usb_audio_channel_count) { mp_raise_ValueError_varg(MP_ERROR_TEXT("The sample's %q does not match"), MP_QSTR_channel_count); } - if (audiosample_get_bits_per_sample(sample_base) != usb_audio_bits_per_sample) { + if (audiosample_get_bits_per_sample(sample_base) != USB_AUDIO_BITS_PER_SAMPLE) { mp_raise_ValueError_varg(MP_ERROR_TEXT("The sample's %q does not match"), MP_QSTR_bits_per_sample); } if (!sample_base->samples_signed) { @@ -107,9 +108,9 @@ size_t usb_audio_usbmicrophone_background_fill(uint8_t *out, size_t max_bytes) { return 0; } - // The negotiated USB format is 16-bit signed mono PCM. For this step the + // The negotiated USB format is 16-bit signed PCM. For this step the // bound sample is assumed to already be in that format (e.g. a 16-bit signed - // mono audiocore.RawSample), so its bytes are copied straight through. + // audiocore.RawSample), so its bytes are copied straight through. size_t filled = 0; while (filled < max_bytes) { if (self->buffer_length == 0) { @@ -138,11 +139,35 @@ size_t usb_audio_usbmicrophone_background_fill(uint8_t *out, size_t max_bytes) { } } - size_t n = MIN(self->buffer_length, max_bytes - filled); - memcpy(out + filled, self->buffer, n); - self->buffer += n; - self->buffer_length -= n; - filled += n; + // Bytes written to out, and bytes consumed from the sample's chunk. They + // differ when a mono sample feeds the always-stereo USB stream. + size_t written; + size_t consumed; + if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_N_CHANNELS)) { + written = consumed = MIN(self->buffer_length, max_bytes - filled); + memcpy(out + filled, self->buffer, written); + } else { + // Mono source: duplicate each sample into both USB channels. Work in + // whole stereo frames so a partial frame can never be emitted, and + // cap by both what the chunk holds and what still fits in out. + size_t frames = MIN(self->buffer_length / USB_AUDIO_N_BYTES_PER_SAMPLE, + (max_bytes - filled) / USB_AUDIO_BYTES_PER_FRAME); + const int16_t *in = (const int16_t *)(const void *)self->buffer; + int16_t *dest = (int16_t *)(void *)(out + filled); + for (size_t i = 0; i < frames; i++) { + dest[2 * i] = dest[2 * i + 1] = in[i]; + } + written = frames * USB_AUDIO_BYTES_PER_FRAME; + consumed = frames * USB_AUDIO_N_BYTES_PER_SAMPLE; + } + if (written == 0) { + // Less than one whole frame of room left in out (or nothing to copy): + // stop rather than spin. The caller pads the remainder with silence. + break; + } + self->buffer += consumed; + self->buffer_length -= consumed; + filled += written; } return filled; diff --git a/shared-module/usb_audio/USBSpeaker.c b/shared-module/usb_audio/USBSpeaker.c index 12921430bc3..c04ee9b280d 100644 --- a/shared-module/usb_audio/USBSpeaker.c +++ b/shared-module/usb_audio/USBSpeaker.c @@ -32,7 +32,7 @@ void common_hal_usb_audio_usbspeaker_construct(usb_audio_usbspeaker_obj_t *self) // format we present is 16-bit signed LE PCM, which is exactly what the // CircuitPython audio pipeline carries, so no conversion is needed. self->base.sample_rate = usb_audio_sample_rate; - self->base.bits_per_sample = usb_audio_bits_per_sample; + self->base.bits_per_sample = USB_AUDIO_BITS_PER_SAMPLE; self->base.channel_count = usb_audio_channel_count; self->base.samples_signed = true; self->base.single_buffer = false; @@ -148,25 +148,43 @@ uint32_t common_hal_usb_audio_usbspeaker_read(usb_audio_usbspeaker_obj_t *self, // guard) this runs in VM/task context and can be preempted by the USB // producer, so it brackets its read-modify-write with interrupts disabled, // mirroring usb_audio_usbspeaker_background_drain(). - size_t bytes_per_frame = (self->base.bits_per_sample / 8) * self->base.channel_count; - size_t want = (size_t)length * bytes_per_frame; + // The ring holds wire-format frames (always USB_AUDIO_N_CHANNELS channels). + // `length` and the return value count entries in `buffer`, i.e. 16-bit + // samples: a stereo speaker writes one per channel of each frame, a mono one + // keeps only each frame's left channel. + bool mono = usb_audio_channel_count != USB_AUDIO_N_CHANNELS; + size_t samples_per_frame = mono ? 1 : USB_AUDIO_N_CHANNELS; + size_t want = (size_t)(length / samples_per_frame) * USB_AUDIO_BYTES_PER_FRAME; common_hal_mcu_disable_interrupts(); size_t to_copy = MIN(self->ring_count, want); // Never split a frame across calls (the ring always holds whole UAC2 frames, // but stay defensive so the returned count is always a whole number). - to_copy -= to_copy % bytes_per_frame; + to_copy -= to_copy % USB_AUDIO_BYTES_PER_FRAME; size_t first = MIN(to_copy, USB_AUDIO_SPEAKER_RING_SIZE - self->ring_tail); - memcpy(buffer, &self->ring[self->ring_tail], first); - if (to_copy > first) { - memcpy((uint8_t *)buffer + first, &self->ring[0], to_copy - first); + if (MP_LIKELY(!mono)) { + memcpy(buffer, &self->ring[self->ring_tail], first); + if (to_copy > first) { + memcpy((uint8_t *)buffer + first, &self->ring[0], to_copy - first); + } + } else { + int16_t *word_out = (int16_t *)buffer; + const int16_t *word_ring = (const int16_t *)(const void *)self->ring; + size_t base = self->ring_tail / USB_AUDIO_N_BYTES_PER_SAMPLE; + size_t written = 0; + for (size_t i = 0; i < first / USB_AUDIO_N_BYTES_PER_SAMPLE; i += USB_AUDIO_N_CHANNELS) { + word_out[written++] = word_ring[base + i]; + } + for (size_t i = 0; i < (to_copy - first) / USB_AUDIO_N_BYTES_PER_SAMPLE; i += USB_AUDIO_N_CHANNELS) { + word_out[written++] = word_ring[i]; + } } self->ring_tail = (self->ring_tail + to_copy) % USB_AUDIO_SPEAKER_RING_SIZE; self->ring_count -= to_copy; common_hal_mcu_enable_interrupts(); - return to_copy / bytes_per_frame; + return to_copy / USB_AUDIO_BYTES_PER_FRAME * samples_per_frame; } // --------------------------------------------------------------------+ @@ -199,29 +217,51 @@ audioio_get_buffer_result_t usb_audio_usbspeaker_get_buffer(usb_audio_usbspeaker // It is never preempted by the producer, so no interrupt guard is required. size_t to_copy = MIN(self->ring_count, (size_t)half); + // Bytes produced in out. The ring always holds wire-format (stereo) frames; + // a mono sample keeps only the left channel of each, so it produces half. + bool mono = usb_audio_channel_count != USB_AUDIO_N_CHANNELS; + size_t produced = mono ? to_copy / 2 : to_copy; + size_t first = MIN(to_copy, USB_AUDIO_SPEAKER_RING_SIZE - self->ring_tail); - memcpy(out, &self->ring[self->ring_tail], first); - if (to_copy > first) { - memcpy(out + first, &self->ring[0], to_copy - first); + if (MP_LIKELY(!mono)) { + memcpy(out, &self->ring[self->ring_tail], first); + if (to_copy > first) { + memcpy(out + first, &self->ring[0], to_copy - first); + } + } else { + // Take the left sample of each stereo frame, across up to two ring + // segments. Indices are in int16 units, counted from the segment start. + int16_t *word_out = (int16_t *)(void *)out; + const int16_t *word_ring = (const int16_t *)(const void *)self->ring; + size_t base = self->ring_tail / USB_AUDIO_N_BYTES_PER_SAMPLE; + size_t written = 0; + for (size_t i = 0; i < first / USB_AUDIO_N_BYTES_PER_SAMPLE; i += USB_AUDIO_N_CHANNELS) { + word_out[written++] = word_ring[base + i]; + } + for (size_t i = 0; i < (to_copy - first) / USB_AUDIO_N_BYTES_PER_SAMPLE; i += USB_AUDIO_N_CHANNELS) { + word_out[written++] = word_ring[i]; + } } self->ring_tail = (self->ring_tail + to_copy) % USB_AUDIO_SPEAKER_RING_SIZE; self->ring_count -= to_copy; - if (to_copy < half) { + // Bytes the caller will be handed, which is all a mono sample can fill. + uint32_t out_length = mono ? half / 2 : half; + if (produced < out_length) { // Underrun: pad the remainder with silence. Samples are signed, so // silence is 0. This is the consume-side of the pacing failure mode // tracked in the usb-audio-artifact-pacing memory: we never spin. - memset(out + to_copy, 0, half - to_copy); + memset(out + produced, 0, out_length - produced); } - // Mono only for v1, so the single-channel offset is always 0; computed the - // same way as audiocore.RawSample so stereo (interleaved ring) can extend it. + // Computed the same way as audiocore.RawSample so stereo (interleaved ring) + // can extend it. if (single_channel_output) { out += (channel % self->base.channel_count) * (self->base.bits_per_sample / 8); } *buffer = out; - *buffer_length = half; + *buffer_length = out_length; // A live USB stream is infinite; never report DONE or the backend would stop. return GET_BUFFER_MORE_DATA; } diff --git a/shared-module/usb_audio/USBSpeaker.h b/shared-module/usb_audio/USBSpeaker.h index 8c06cff519d..0760676b53a 100644 --- a/shared-module/usb_audio/USBSpeaker.h +++ b/shared-module/usb_audio/USBSpeaker.h @@ -22,7 +22,7 @@ // comfortably inside the ring. #define USB_AUDIO_SPEAKER_FRAMES_PER_BUFFER (128) -// Bytes per audio frame in the negotiated UAC2 format (mono 16-bit for v1). +// Bytes per audio frame in the negotiated UAC2 format (stereo 16-bit). #define USB_AUDIO_SPEAKER_BYTES_PER_FRAME (USB_AUDIO_N_BYTES_PER_SAMPLE * USB_AUDIO_N_CHANNELS) // Full owned double-buffer: two halves of FRAMES_PER_BUFFER frames each. Matches diff --git a/shared-module/usb_audio/__init__.c b/shared-module/usb_audio/__init__.c index cac47bbc5fe..78eac9fdb5b 100644 --- a/shared-module/usb_audio/__init__.c +++ b/shared-module/usb_audio/__init__.c @@ -17,9 +17,20 @@ #include "py/misc.h" #include "py/mphal.h" #include "py/runtime.h" +#include "supervisor/port.h" #include "supervisor/shared/tick.h" +#include "supervisor/usb.h" #include "tusb.h" +// Scratch chunk the microphone task hands to TinyUSB. It is written from the USB +// background task for as long as the device is enabled, which outlives every VM, +// so it comes from the port heap rather than the MicroPython heap. Sized by +// enable() for 1 ms at the negotiated rate, in the wire format (always +// USB_AUDIO_N_CHANNELS channels), so a board that never calls enable() pays +// nothing for it. +static int16_t *usb_audio_mic_samples; +static size_t usb_audio_mic_samples_len; + static bool usb_audio_is_enabled = false; // The host opens each AudioStreaming interface independently (alt 0 = idle, alt 1 @@ -36,7 +47,6 @@ static uint8_t usb_audio_spk_as_itf = 0xff; uint32_t usb_audio_sample_rate; uint8_t usb_audio_channel_count; -uint8_t usb_audio_bits_per_sample; bool usb_audio_microphone_enabled; bool usb_audio_speaker_enabled; @@ -44,14 +54,24 @@ bool usb_audio_speaker_enabled; static int8_t usb_audio_mute[USB_AUDIO_N_CHANNELS + 1]; static int16_t usb_audio_volume[USB_AUDIO_N_CHANNELS + 1]; -bool shared_module_usb_audio_enable(mp_int_t sample_rate, mp_int_t channel_count, mp_int_t bits_per_sample, bool microphone, bool speaker) { +bool shared_module_usb_audio_enable(mp_int_t sample_rate, mp_int_t channel_count, bool microphone, bool speaker) { if (tud_connected()) { return false; } + // One scratch chunk (1 ms at the sample rate); we loop until the FIFO reaches + // the setpoint. enable() may be called more than once, so release any chunk + // sized for a previous rate before taking a new one. + port_free(usb_audio_mic_samples); + usb_audio_mic_samples_len = sample_rate / 1000 * USB_AUDIO_BYTES_PER_FRAME; + usb_audio_mic_samples = port_malloc_zero(usb_audio_mic_samples_len, false); + if (usb_audio_mic_samples == NULL) { + usb_audio_mic_samples_len = 0; + return false; + } + usb_audio_sample_rate = sample_rate; usb_audio_channel_count = channel_count; - usb_audio_bits_per_sample = bits_per_sample; usb_audio_microphone_enabled = microphone; usb_audio_speaker_enabled = speaker; usb_audio_is_enabled = true; @@ -64,6 +84,9 @@ bool shared_module_usb_audio_disable(void) { return false; } usb_audio_is_enabled = false; + port_free(usb_audio_mic_samples); + usb_audio_mic_samples = NULL; + usb_audio_mic_samples_len = 0; return true; } @@ -126,8 +149,8 @@ void usb_audio_setup_singletons(void) { speaker; } -// Hand-rolled UAC2 mono speaker (host -> board) descriptor WITHOUT an async -// feedback endpoint. This mirrors TinyUSB's TUD_AUDIO20_SPEAKER_MONO_FB_DESCRIPTOR +// Hand-rolled UAC2 speaker (host -> board) descriptor WITHOUT an async +// feedback endpoint. This mirrors TinyUSB's TUD_AUDIO20_SPEAKER_STEREO_FB_DESCRIPTOR // (lib/tinyusb/src/device/usbd.h) but drops the trailing feedback endpoint, so // the streaming alt-setting declares a single OUT endpoint (_nEPs = 0x01). The // entity IDs match the mic descriptor (see usb_audio_descriptors.h); only the @@ -140,7 +163,7 @@ void usb_audio_setup_singletons(void) { /* Standard AC Interface Descriptor(4.7.1) */ \ TUD_AUDIO20_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx), \ /* Class-Specific AC Interface Header Descriptor(4.7.2) */ \ - TUD_AUDIO20_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO20_FUNC_DESKTOP_SPEAKER, /*_totallen*/ TUD_AUDIO20_DESC_CLK_SRC_LEN + TUD_AUDIO20_DESC_INPUT_TERM_LEN + TUD_AUDIO20_DESC_OUTPUT_TERM_LEN + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(1), /*_ctrl*/ AUDIO20_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ + TUD_AUDIO20_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO20_FUNC_DESKTOP_SPEAKER, /*_totallen*/ TUD_AUDIO20_DESC_CLK_SRC_LEN + TUD_AUDIO20_DESC_INPUT_TERM_LEN + TUD_AUDIO20_DESC_OUTPUT_TERM_LEN + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(2), /*_ctrl*/ AUDIO20_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ /* Clock Source Descriptor(4.7.2.1) */ \ TUD_AUDIO20_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO20_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO20_CTRL_R << AUDIO20_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00), \ /* Input Terminal Descriptor(4.7.2.4) -- USB streaming in from the host */ \ @@ -148,7 +171,7 @@ void usb_audio_setup_singletons(void) { /* Output Terminal Descriptor(4.7.2.5) -- desktop speaker */ \ TUD_AUDIO20_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_srcid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ /* Feature Unit Descriptor(4.7.2.8) */ \ - TUD_AUDIO20_DESC_FEATURE_UNIT(/*_unitid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00, /*_ctrlch0master*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS), \ + TUD_AUDIO20_DESC_FEATURE_UNIT(/*_unitid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00, /*_ctrlch0master*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS), \ /* Standard AS Interface Descriptor(4.9.1) -- alt 0, zero bandwidth */ \ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00), \ /* Standard AS Interface Descriptor(4.9.1) -- alt 1, one OUT endpoint */ \ @@ -162,10 +185,45 @@ void usb_audio_setup_singletons(void) { /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ \ TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000) -// Hand-rolled UAC2 mono headset (microphone + speaker both enabled): one audio function +// Hand-rolled UAC2 microphone (board -> host) descriptor WITHOUT an async +// feedback endpoint. This mirrors TinyUSB's TUD_AUDIO20_MIC_ONE_CH_DESCRIPTOR +// (lib/tinyusb/src/device/usbd.h) but drops the trailing feedback endpoint, so +// the streaming alt-setting declares a single IN endpoint (_nEPs = 0x00). Async +// feedback for true clock matching is a later step. +#define USB_AUDIO_MIC_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \ + /* Standard Interface Association Descriptor (IAD) */ \ + TUD_AUDIO20_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00), \ + /* Standard AC Interface Descriptor(4.7.1) */ \ + TUD_AUDIO20_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx), \ + /* Class-Specific AC Interface Header Descriptor(4.7.2) */ \ + TUD_AUDIO20_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO20_FUNC_MICROPHONE, /*_totallen*/ TUD_AUDIO20_DESC_CLK_SRC_LEN + TUD_AUDIO20_DESC_INPUT_TERM_LEN + TUD_AUDIO20_DESC_OUTPUT_TERM_LEN + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(2), /*_ctrl*/ AUDIO20_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ + /* Clock Source Descriptor(4.7.2.1) */ \ + TUD_AUDIO20_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO20_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO20_CTRL_R << AUDIO20_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00), \ + /* Input Terminal Descriptor(4.7.2.4) -- microphone */ \ + TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO20_CTRL_R << AUDIO20_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ + /* Output Terminal Descriptor(4.7.2.5) -- USB streaming */ \ + TUD_AUDIO20_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_srcid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ + /* Feature Unit Descriptor(4.7.2.8) */ \ + TUD_AUDIO20_DESC_FEATURE_UNIT(/*_unitid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00, /*_ctrlch0master*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS), \ + /* Standard AS Interface Descriptor(4.9.1) */ \ + /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */ \ + TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00), \ + /* Standard AS Interface Descriptor(4.9.1) */ \ + /* Interface 1, Alternate 1 - alternate interface for data streaming */ \ + TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00), \ + /* Class-Specific AS Interface Descriptor(4.9.2) */ \ + TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ + /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ + TUD_AUDIO20_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ + /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ + TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t)((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01), \ + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ \ + TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000) + +// Hand-rolled UAC2 headset (microphone + speaker both enabled): one audio function // presenting both a speaker (host -> board OUT) and a microphone (board -> host // IN) at once. This combines USB_AUDIO_SPEAKER_DESCRIPTOR's speaker chain with -// TUD_AUDIO20_MIC_ONE_CH_DESCRIPTOR's mic chain under a single IAD. The two chains +// USB_AUDIO_MIC_DESCRIPTOR's mic chain under a single IAD. The two chains // must use distinct entity IDs (USB_AUDIO_HS_ENTITY_*; see usb_audio_descriptors.h) // because they live in the same AudioControl interface, and they share one clock // source. The function spans three interfaces: AudioControl (_itfnum), the @@ -178,21 +236,21 @@ void usb_audio_setup_singletons(void) { /* Standard AC Interface Descriptor(4.7.1) */ \ TUD_AUDIO20_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx), \ /* Class-Specific AC Interface Header Descriptor(4.7.2) -- clock + both chains */ \ - TUD_AUDIO20_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO20_FUNC_HEADSET, /*_totallen*/ TUD_AUDIO20_DESC_CLK_SRC_LEN + 2 * (TUD_AUDIO20_DESC_INPUT_TERM_LEN + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(1) + TUD_AUDIO20_DESC_OUTPUT_TERM_LEN), /*_ctrl*/ AUDIO20_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ + TUD_AUDIO20_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO20_FUNC_HEADSET, /*_totallen*/ TUD_AUDIO20_DESC_CLK_SRC_LEN + 2 * (TUD_AUDIO20_DESC_INPUT_TERM_LEN + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(2) + TUD_AUDIO20_DESC_OUTPUT_TERM_LEN), /*_ctrl*/ AUDIO20_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ /* Clock Source Descriptor(4.7.2.1) -- shared by both chains */ \ TUD_AUDIO20_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO20_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO20_CTRL_R << AUDIO20_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x00, /*_stridx*/ 0x00), \ /* --- Speaker chain (host -> board) --- */ \ /* Input Terminal Descriptor(4.7.2.4) -- USB streaming in from the host */ \ TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO20_CTRL_R << AUDIO20_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ /* Feature Unit Descriptor(4.7.2.8) */ \ - TUD_AUDIO20_DESC_FEATURE_UNIT(/*_unitid*/ USB_AUDIO_HS_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_stridx*/ 0x00, /*_ctrlch0master*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS), \ + TUD_AUDIO20_DESC_FEATURE_UNIT(/*_unitid*/ USB_AUDIO_HS_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_stridx*/ 0x00, /*_ctrlch0master*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS), \ /* Output Terminal Descriptor(4.7.2.5) -- desktop speaker */ \ TUD_AUDIO20_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ 0x00, /*_srcid*/ USB_AUDIO_HS_ENTITY_SPK_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ /* --- Mic chain (board -> host) --- */ \ /* Input Terminal Descriptor(4.7.2.4) -- generic microphone */ \ TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_MIC_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO20_CTRL_R << AUDIO20_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ /* Feature Unit Descriptor(4.7.2.8) */ \ - TUD_AUDIO20_DESC_FEATURE_UNIT(/*_unitid*/ USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_HS_ENTITY_MIC_INPUT_TERMINAL, /*_stridx*/ 0x00, /*_ctrlch0master*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS), \ + TUD_AUDIO20_DESC_FEATURE_UNIT(/*_unitid*/ USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_HS_ENTITY_MIC_INPUT_TERMINAL, /*_stridx*/ 0x00, /*_ctrlch0master*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS), \ /* Output Terminal Descriptor(4.7.2.5) -- USB streaming out to the host */ \ TUD_AUDIO20_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_MIC_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_srcid*/ USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ /* --- Speaker AudioStreaming interface (_itfnum + 1) --- */ \ @@ -240,7 +298,7 @@ size_t usb_audio_descriptor_length(void) { if (usb_audio_direction_is_output()) { return USB_AUDIO_SPEAKER_DESC_LEN; } - return TUD_AUDIO20_MIC_ONE_CH_DESC_LEN; + return USB_AUDIO_MIC_DESC_LEN; } size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *descriptor_counts, uint8_t *current_interface_string) { @@ -256,18 +314,23 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de if (usb_audio_direction_is_input_output()) { // Combined headset: a speaker AudioStreaming interface (OUT) and a mic - // AudioStreaming interface (IN) under one AudioControl interface. This - // needs two isochronous endpoints, so it cannot be served by ports that - // pin ISO to a single dedicated endpoint number (forced_iso_ep, e.g. - // nRF52, which has only one ISO-capable endpoint). On those ports the - // sequential numbers below will not match the hardware's required ISO - // endpoint and the stream will not open; INPUT_OUTPUT is effectively - // unsupported there. The sequential-allocation ports (e.g. RP2) take a - // distinct number for each direction. - const uint8_t ep_out = descriptor_counts->current_endpoint; - const uint8_t ep_in = descriptor_counts->current_endpoint + 1; + // AudioStreaming interface (IN) under one AudioControl interface, so one + // isochronous endpoint in each direction. + // + // Sequential-allocation ports (e.g. RP2) take a distinct number for each + // direction. Ports that pin ISO to a dedicated endpoint number + // (forced_iso_ep, e.g. nRF52) use that one number for BOTH directions: + // the constraint there is the endpoint *number*, not the direction. The + // nRF52 USBD has a separate ISOIN and ISOOUT on endpoint 8, and TinyUSB + // splits the ISO buffer (ISOSPLIT = HalfIN) when both are open, so 0x08 + // and 0x88 can run at the same time. As in the single-direction branches + // below, the dedicated ISO endpoint is separate hardware and must not + // consume the sequential endpoint numbers the other interfaces draw from. + const uint8_t ep_out = forced_iso_ep ? iso_ep_num : descriptor_counts->current_endpoint; + const uint8_t ep_in = forced_iso_ep ? iso_ep_num : (descriptor_counts->current_endpoint + 1); usb_add_interface_string(*current_interface_string, "CircuitPython Headset"); + const uint8_t usb_audio_descriptor[] = { USB_AUDIO_HEADSET_DESCRIPTOR( /*_itfnum*/ descriptor_counts->current_interface, @@ -284,12 +347,17 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de usb_audio_mic_as_itf = descriptor_counts->current_interface + 2; (*current_interface_string)++; - // One IAD wrapping an AudioControl + two AudioStreaming interfaces, plus - // one OUT and one IN endpoint. + // One IAD wrapping an AudioControl + two AudioStreaming interfaces, plus a + // single isochronous endpoint in each direction. On sequential-allocation + // ports the two directions take separate endpoint numbers, consuming two of + // the port's endpoint pairs. On forced_iso_ep ports both directions share + // the dedicated ISO endpoint, which is separate hardware and consumes none. descriptor_counts->current_interface += 3; - descriptor_counts->num_out_endpoints++; - descriptor_counts->num_in_endpoints++; - descriptor_counts->current_endpoint += 2; + if (!forced_iso_ep) { + descriptor_counts->num_out_endpoints += 1; + descriptor_counts->num_in_endpoints += 1; + descriptor_counts->current_endpoint += 2; + } memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); @@ -298,6 +366,10 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de if (usb_audio_direction_is_output()) { usb_add_interface_string(*current_interface_string, "CircuitPython Speaker"); + + // The AudioStreaming interface follows the AudioControl interface. + usb_audio_spk_as_itf = descriptor_counts->current_interface + 1; + const uint8_t usb_audio_descriptor[] = { USB_AUDIO_SPEAKER_DESCRIPTOR( /*_itfnum*/ descriptor_counts->current_interface, @@ -308,15 +380,13 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX) }; - // The AudioStreaming interface follows the AudioControl interface. - usb_audio_spk_as_itf = descriptor_counts->current_interface + 1; - (*current_interface_string)++; - // One IAD wrapping an AudioControl + an AudioStreaming interface, plus one OUT endpoint. + // One IAD wrapping an AudioControl + an AudioStreaming interface, plus a + // single isochronous OUT endpoint. descriptor_counts->current_interface += 2; if (!forced_iso_ep) { - descriptor_counts->num_out_endpoints++; - descriptor_counts->current_endpoint++; + descriptor_counts->num_out_endpoints += 1; + descriptor_counts->current_endpoint += 1; } memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); @@ -325,8 +395,12 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de } usb_add_interface_string(*current_interface_string, "CircuitPython Microphone"); + + // The AudioStreaming interface follows the AudioControl interface. + usb_audio_mic_as_itf = descriptor_counts->current_interface + 1; + const uint8_t usb_audio_descriptor[] = { - TUD_AUDIO20_MIC_ONE_CH_DESCRIPTOR( + USB_AUDIO_MIC_DESCRIPTOR( /*_itfnum*/ descriptor_counts->current_interface, /*_stridx*/ *current_interface_string, /*_nBytesPerSample*/ USB_AUDIO_N_BYTES_PER_SAMPLE, @@ -335,15 +409,13 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX) }; - // The AudioStreaming interface follows the AudioControl interface. - usb_audio_mic_as_itf = descriptor_counts->current_interface + 1; - (*current_interface_string)++; - // One IAD wrapping an AudioControl + an AudioStreaming interface, plus one IN endpoint. + // One IAD wrapping an AudioControl + an AudioStreaming interface, plus a + // single isochronous IN endpoint. descriptor_counts->current_interface += 2; if (!forced_iso_ep) { - descriptor_counts->num_in_endpoints++; - descriptor_counts->current_endpoint++; + descriptor_counts->num_in_endpoints += 1; + descriptor_counts->current_endpoint += 1; } memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); @@ -397,24 +469,20 @@ static void usb_audio_microphone_task(void) { tu_fifo_t *ep_in_ff = tud_audio_get_ep_in_ff(); uint16_t const target = tu_fifo_depth(ep_in_ff) / 2; - // One scratch chunk (1 ms at the max rate); we loop until the FIFO reaches - // the setpoint. Sized for the highest rate enable() accepts. - static int16_t samples[USB_AUDIO_MAX_SAMPLE_RATE / 1000 * USB_AUDIO_N_CHANNELS]; - uint16_t count; while ((count = tu_fifo_count(ep_in_ff)) < target) { size_t want = target - count; - if (want > sizeof(samples)) { - want = sizeof(samples); + if (want > usb_audio_mic_samples_len) { + want = usb_audio_mic_samples_len; } // Pull the next chunk from whichever USBMicrophone is playing. bool underran = false; - size_t filled = usb_audio_usbmicrophone_background_fill((uint8_t *)samples, want); + size_t filled = usb_audio_usbmicrophone_background_fill((uint8_t *)usb_audio_mic_samples, want); if (filled == 0) { // No source attached, paused, or fully drained: keep the endpoint // alive with silence so the host never sees a starved stream. - memset((uint8_t *)samples, 0, want); + memset((uint8_t *)usb_audio_mic_samples, 0, want); } else if (filled < want) { // The source momentarily underran. Send just what it produced and // let the FIFO cushion ride until it catches up next pass, rather @@ -423,7 +491,7 @@ static void usb_audio_microphone_task(void) { underran = true; } - if (tud_audio_write((uint8_t *)samples, (uint16_t)want) == 0) { + if (tud_audio_write((uint8_t *)usb_audio_mic_samples, (uint16_t)want) == 0) { break; // FIFO unexpectedly full / host not ready } if (underran) { @@ -465,10 +533,33 @@ bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_reques usb_audio_usbspeaker_streaming_reset(); } else if (itf == usb_audio_mic_as_itf) { usb_audio_mic_streaming = streaming; + if (streaming) { + // Prime the FIFO for the first frame; after that each completed + // transfer schedules the next refill via tud_audio_tx_done_isr(). + usb_background_schedule(); + } } return true; } +// Invoked from the audio class transfer-complete path once the next packet has +// been loaded into the EP IN buffer. TinyUSB services audio completions in ISR +// context, so they never queue a USB event. A port that pumps the optional class +// tasks from a task loop sitting behind tud_task() therefore never refills the +// microphone FIFO. Scheduling the refill here keeps usb_audio independent of how +// a port drives TinyUSB. +bool tud_audio_tx_done_isr(uint8_t rhport, uint16_t n_bytes_sent, uint8_t func_id, + uint8_t ep_in, uint8_t cur_alt_setting) { + (void)rhport; + (void)n_bytes_sent; + (void)func_id; + (void)ep_in; + (void)cur_alt_setting; + + usb_background_schedule(); + return true; +} + bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void)rhport; uint8_t const itf = (uint8_t)tu_u16_low(p_request->wIndex); @@ -496,7 +587,7 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p // A headset exposes a feature unit per direction; the speaker's id matches the // single-direction USB_AUDIO_ENTITY_FEATURE_UNIT, the mic adds a second one. - // Mute/volume state is shared across them (mono, cosmetic for now). + // Mute/volume state is shared across them. if (entityID == USB_AUDIO_ENTITY_FEATURE_UNIT || entityID == USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT) { if (channelNum > USB_AUDIO_N_CHANNELS) { diff --git a/shared-module/usb_audio/__init__.h b/shared-module/usb_audio/__init__.h index 8ec13f90853..5730e6ef60b 100644 --- a/shared-module/usb_audio/__init__.h +++ b/shared-module/usb_audio/__init__.h @@ -17,7 +17,7 @@ // called before USB is connected (i.e. from boot.py); they return false // otherwise. At least one of microphone/speaker must be true; enabling both // presents a combined headset. -bool shared_module_usb_audio_enable(mp_int_t sample_rate, mp_int_t channel_count, mp_int_t bits_per_sample, bool microphone, bool speaker); +bool shared_module_usb_audio_enable(mp_int_t sample_rate, mp_int_t channel_count, bool microphone, bool speaker); bool shared_module_usb_audio_disable(void); // True once enable() has been called successfully. @@ -36,7 +36,6 @@ bool usb_audio_speaker_streaming(void); // Negotiated audio format, valid when usb_audio_enabled() is true. extern uint32_t usb_audio_sample_rate; extern uint8_t usb_audio_channel_count; -extern uint8_t usb_audio_bits_per_sample; // Which streams were requested in enable(), valid when usb_audio_enabled() is // true. Both true presents a combined headset. diff --git a/shared-module/usb_audio/usb_audio_descriptors.h b/shared-module/usb_audio/usb_audio_descriptors.h index 18d72dcabc5..8fba956a2a6 100644 --- a/shared-module/usb_audio/usb_audio_descriptors.h +++ b/shared-module/usb_audio/usb_audio_descriptors.h @@ -26,12 +26,17 @@ size_t usb_audio_descriptor_length(void); // The isochronous IN endpoint's wMaxPacketSize in the USB descriptor is computed // for this rate, so it is the highest rate usb_audio.enable() will accept. #define USB_AUDIO_MAX_SAMPLE_RATE (48000) +#define USB_AUDIO_N_CHANNELS (2) -// 16-bit signed LE PCM, mono. +// 16-bit signed LE PCM. #define USB_AUDIO_N_BYTES_PER_SAMPLE (2) -#define USB_AUDIO_N_CHANNELS (1) #define USB_AUDIO_BITS_PER_SAMPLE (USB_AUDIO_N_BYTES_PER_SAMPLE * 8) +// Bytes per audio frame on the wire. The descriptors always declare +// USB_AUDIO_N_CHANNELS channels, whatever channel_count the sample has, so this +// is the granularity every buffer handed to/from TinyUSB must be a multiple of. +#define USB_AUDIO_BYTES_PER_FRAME (USB_AUDIO_N_BYTES_PER_SAMPLE * USB_AUDIO_N_CHANNELS) + // Endpoint number for the single isochronous audio data endpoint. Most device // controllers accept an ISO endpoint on any number, so the descriptor builder // just takes the next sequential one (signalled by 0 here). The nRF52 USBD, @@ -44,7 +49,7 @@ size_t usb_audio_descriptor_length(void); #define USB_AUDIO_ISO_EP_NUM (0) #endif -// Fixed UAC2 entity IDs baked into TUD_AUDIO20_MIC_ONE_CH_DESCRIPTOR and the +// Fixed UAC2 entity IDs baked into USB_AUDIO_MIC_DESCRIPTOR and the // hand-rolled speaker descriptor (USB_AUDIO_SPEAKER_DESCRIPTOR in __init__.c). // The speaker reuses the same IDs as the mic; only the terminal roles reverse // (input terminal = USB streaming, output terminal = desktop speaker). @@ -67,10 +72,10 @@ size_t usb_audio_descriptor_length(void); #define USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT (0x06) #define USB_AUDIO_HS_ENTITY_MIC_OUTPUT_TERMINAL (0x07) // USB streaming out to host -// Length of the no-feedback mono speaker descriptor. It uses the same set of -// sub-descriptors as TUD_AUDIO20_MIC_ONE_CH_DESCRIPTOR (one isochronous data +// Length of the no-feedback speaker descriptor. It uses the same set of +// sub-descriptors as USB_AUDIO_MIC_DESCRIPTOR (except one isochronous data // endpoint, no feedback endpoint), so this is identical to -// TUD_AUDIO20_MIC_ONE_CH_DESC_LEN -- but spell it out independently so the two +// USB_AUDIO_MIC_DESC_LEN -- but spell it out independently so the two // can diverge later (e.g. stereo) without silently mis-sizing the descriptor. // These TUD_AUDIO20_DESC_*_LEN macros come from TinyUSB's usbd.h; this expression // is only expanded where that header is already included (never at the point @@ -81,7 +86,23 @@ size_t usb_audio_descriptor_length(void); + TUD_AUDIO20_DESC_CLK_SRC_LEN \ + TUD_AUDIO20_DESC_INPUT_TERM_LEN \ + TUD_AUDIO20_DESC_OUTPUT_TERM_LEN \ - + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(1) \ + + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(2) \ + + TUD_AUDIO20_DESC_STD_AS_LEN \ + + TUD_AUDIO20_DESC_STD_AS_LEN \ + + TUD_AUDIO20_DESC_CS_AS_INT_LEN \ + + TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN \ + + TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN \ + + TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN) + +#define USB_AUDIO_MIC_DESC_LEN (TUD_AUDIO20_DESC_IAD_LEN \ + + TUD_AUDIO20_DESC_STD_AC_LEN \ + + TUD_AUDIO20_DESC_CS_AC_LEN \ + + TUD_AUDIO20_DESC_CLK_SRC_LEN \ + /* mic chain: microphone input terminal -> feature unit -> USB-streaming out */ \ + + TUD_AUDIO20_DESC_INPUT_TERM_LEN \ + + TUD_AUDIO20_DESC_OUTPUT_TERM_LEN \ + + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(2) \ + /* mic AudioStreaming interface (alt 0 + alt 1 with IN endpoint) */ \ + TUD_AUDIO20_DESC_STD_AS_LEN \ + TUD_AUDIO20_DESC_STD_AS_LEN \ + TUD_AUDIO20_DESC_CS_AS_INT_LEN \ @@ -104,11 +125,11 @@ size_t usb_audio_descriptor_length(void); + TUD_AUDIO20_DESC_CLK_SRC_LEN \ /* speaker chain: USB-streaming input terminal -> feature unit -> speaker */ \ + TUD_AUDIO20_DESC_INPUT_TERM_LEN \ - + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(1) \ + + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(2) \ + TUD_AUDIO20_DESC_OUTPUT_TERM_LEN \ /* mic chain: microphone input terminal -> feature unit -> USB-streaming out */ \ + TUD_AUDIO20_DESC_INPUT_TERM_LEN \ - + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(1) \ + + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(2) \ + TUD_AUDIO20_DESC_OUTPUT_TERM_LEN \ /* speaker AudioStreaming interface (alt 0 + alt 1 with OUT endpoint) */ \ + TUD_AUDIO20_DESC_STD_AS_LEN \ diff --git a/shared-module/usb_hid/Device.c b/shared-module/usb_hid/Device.c index d6c4697210f..42f5790123e 100644 --- a/shared-module/usb_hid/Device.c +++ b/shared-module/usb_hid/Device.c @@ -12,45 +12,10 @@ #include "shared-bindings/usb_hid/Device.h" #include "shared-module/usb_hid/__init__.h" #include "shared-module/usb_hid/Device.h" +#include "shared-module/usb_hid/report_descriptors.h" #include "supervisor/shared/tick.h" #include "tusb.h" -static const uint8_t keyboard_report_descriptor[] = { - 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) - 0x09, 0x06, // Usage (Keyboard) - 0xA1, 0x01, // Collection (Application) - 0x85, 0x01, // Report ID (1) - 0x05, 0x07, // Usage Page (Kbrd/Keypad) - 0x19, 0xE0, // Usage Minimum (0xE0) - 0x29, 0xE7, // Usage Maximum (0xE7) - 0x15, 0x00, // Logical Minimum (0) - 0x25, 0x01, // Logical Maximum (1) - 0x75, 0x01, // Report Size (1) - 0x95, 0x08, // Report Count (8) - 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x95, 0x01, // Report Count (1) - 0x75, 0x08, // Report Size (8) - 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x95, 0x05, // Report Count (5) - 0x75, 0x01, // Report Size (1) - 0x05, 0x08, // Usage Page (LEDs) - 0x19, 0x01, // Usage Minimum (Num Lock) - 0x29, 0x05, // Usage Maximum (Kana) - 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x95, 0x01, // Report Count (1) - 0x75, 0x03, // Report Size (3) - 0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x95, 0x06, // Report Count (6) - 0x75, 0x08, // Report Size (8) - 0x15, 0x00, // Logical Minimum (0) - 0x26, 0xFF, 0x00, // Logical Maximum (255) - 0x05, 0x07, // Usage Page (Kbrd/Keypad) - 0x19, 0x00, // Usage Minimum (0x00) - 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF) - 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0xC0, // End Collection -}; - const usb_hid_device_obj_t usb_hid_device_keyboard_obj = { .base = { .type = &usb_hid_device_type, @@ -65,42 +30,6 @@ const usb_hid_device_obj_t usb_hid_device_keyboard_obj = { .out_report_lengths = { 1, }, }; -static const uint8_t mouse_report_descriptor[] = { - 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) - 0x09, 0x02, // Usage (Mouse) - 0xA1, 0x01, // Collection (Application) - 0x09, 0x01, // Usage (Pointer) - 0xA1, 0x00, // Collection (Physical) - 0x85, 0x02, // 10, 11 Report ID (2) - 0x05, 0x09, // Usage Page (Button) - 0x19, 0x01, // Usage Minimum (0x01) - 0x29, 0x05, // Usage Maximum (0x05) - 0x15, 0x00, // Logical Minimum (0) - 0x25, 0x01, // Logical Maximum (1) - 0x95, 0x05, // Report Count (5) - 0x75, 0x01, // Report Size (1) - 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x95, 0x01, // Report Count (1) - 0x75, 0x03, // Report Size (3) - 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) - 0x09, 0x30, // Usage (X) - 0x09, 0x31, // Usage (Y) - 0x15, 0x81, // Logical Minimum (-127) - 0x25, 0x7F, // Logical Maximum (127) - 0x75, 0x08, // Report Size (8) - 0x95, 0x02, // Report Count (2) - 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) - 0x09, 0x38, // Usage (Wheel) - 0x15, 0x81, // Logical Minimum (-127) - 0x25, 0x7F, // Logical Maximum (127) - 0x75, 0x08, // Report Size (8) - 0x95, 0x01, // Report Count (1) - 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) - 0xC0, // End Collection - 0xC0, // End Collection -}; - const usb_hid_device_obj_t usb_hid_device_mouse_obj = { .base = { .type = &usb_hid_device_type, @@ -115,21 +44,6 @@ const usb_hid_device_obj_t usb_hid_device_mouse_obj = { .out_report_lengths = { 0, }, }; -static const uint8_t consumer_control_report_descriptor[] = { - 0x05, 0x0C, // Usage Page (Consumer) - 0x09, 0x01, // Usage (Consumer Control) - 0xA1, 0x01, // Collection (Application) - 0x85, 0x03, // Report ID (3) - 0x75, 0x10, // Report Size (16) - 0x95, 0x01, // Report Count (1) - 0x15, 0x01, // Logical Minimum (1) - 0x26, 0x8C, 0x02, // Logical Maximum (652) - 0x19, 0x01, // Usage Minimum (Consumer Control) - 0x2A, 0x8C, 0x02, // Usage Maximum (AC Send) - 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0xC0, // End Collection -}; - const usb_hid_device_obj_t usb_hid_device_consumer_control_obj = { .base = { .type = &usb_hid_device_type, diff --git a/shared-module/usb_hid/report_descriptors.c b/shared-module/usb_hid/report_descriptors.c new file mode 100644 index 00000000000..fe629153c78 --- /dev/null +++ b/shared-module/usb_hid/report_descriptors.c @@ -0,0 +1,96 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2018 hathach for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +// Standard HID report descriptors shared between ports. + +#include "shared-module/usb_hid/report_descriptors.h" + +const uint8_t keyboard_report_descriptor[] = { + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x09, 0x06, // Usage (Keyboard) + 0xA1, 0x01, // Collection (Application) + 0x85, 0x01, // Report ID (1) + 0x05, 0x07, // Usage Page (Kbrd/Keypad) + 0x19, 0xE0, // Usage Minimum (0xE0) + 0x29, 0xE7, // Usage Maximum (0xE7) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x08, // Report Count (8) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x95, 0x01, // Report Count (1) + 0x75, 0x08, // Report Size (8) + 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x95, 0x05, // Report Count (5) + 0x75, 0x01, // Report Size (1) + 0x05, 0x08, // Usage Page (LEDs) + 0x19, 0x01, // Usage Minimum (Num Lock) + 0x29, 0x05, // Usage Maximum (Kana) + 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0x95, 0x01, // Report Count (1) + 0x75, 0x03, // Report Size (3) + 0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0x95, 0x06, // Report Count (6) + 0x75, 0x08, // Report Size (8) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x05, 0x07, // Usage Page (Kbrd/Keypad) + 0x19, 0x00, // Usage Minimum (0x00) + 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF) + 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0xC0, // End Collection +}; + +const uint8_t mouse_report_descriptor[] = { + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x09, 0x02, // Usage (Mouse) + 0xA1, 0x01, // Collection (Application) + 0x09, 0x01, // Usage (Pointer) + 0xA1, 0x00, // Collection (Physical) + 0x85, 0x02, // 10, 11 Report ID (2) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (0x01) + 0x29, 0x05, // Usage Maximum (0x05) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x95, 0x05, // Report Count (5) + 0x75, 0x01, // Report Size (1) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x95, 0x01, // Report Count (1) + 0x75, 0x03, // Report Size (3) + 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x15, 0x81, // Logical Minimum (-127) + 0x25, 0x7F, // Logical Maximum (127) + 0x75, 0x08, // Report Size (8) + 0x95, 0x02, // Report Count (2) + 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) + 0x09, 0x38, // Usage (Wheel) + 0x15, 0x81, // Logical Minimum (-127) + 0x25, 0x7F, // Logical Maximum (127) + 0x75, 0x08, // Report Size (8) + 0x95, 0x01, // Report Count (1) + 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) + 0xC0, // End Collection + 0xC0, // End Collection +}; + +const uint8_t consumer_control_report_descriptor[] = { + 0x05, 0x0C, // Usage Page (Consumer) + 0x09, 0x01, // Usage (Consumer Control) + 0xA1, 0x01, // Collection (Application) + 0x85, 0x03, // Report ID (3) + 0x75, 0x10, // Report Size (16) + 0x95, 0x01, // Report Count (1) + 0x15, 0x01, // Logical Minimum (1) + 0x26, 0x8C, 0x02, // Logical Maximum (652) + 0x19, 0x01, // Usage Minimum (Consumer Control) + 0x2A, 0x8C, 0x02, // Usage Maximum (AC Send) + 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0xC0, // End Collection +}; diff --git a/shared-module/usb_hid/report_descriptors.h b/shared-module/usb_hid/report_descriptors.h new file mode 100644 index 00000000000..94a9a752f6a --- /dev/null +++ b/shared-module/usb_hid/report_descriptors.h @@ -0,0 +1,19 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2018 hathach for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +// Standard HID report descriptors shared between ports. These are plain byte +// arrays with no dependencies, so ports using TinyUSB and ports using other +// USB stacks can both use them. They are defined in report_descriptors.c. +// The explicit sizes let callers use sizeof() and are checked by the compiler +// against the definitions. + +#pragma once + +#include + +extern const uint8_t keyboard_report_descriptor[67]; +extern const uint8_t mouse_report_descriptor[64]; +extern const uint8_t consumer_control_report_descriptor[25]; diff --git a/supervisor/shared/bluetooth/bluetooth.c b/supervisor/shared/bluetooth/bluetooth.c index 263aba7cbc9..74edcbb46bb 100644 --- a/supervisor/shared/bluetooth/bluetooth.c +++ b/supervisor/shared/bluetooth/bluetooth.c @@ -252,6 +252,11 @@ void supervisor_bluetooth_init(void) { } #endif // !CIRCUITPY_USB_DEVICE + // A press must begin inside this interval. The safe mode interval just before + // this one also uses the boot button, and a press meant for safe mode can + // still be held down when this interval starts; entering discovery mode from + // it would erase bonding unasked. Require a release first. + bool button_released = !port_boot_button_pressed(); while (diff < 1000) { #if CIRCUITPY_STATUS_LED // Blink on for 50 and off for 100 @@ -262,9 +267,14 @@ void supervisor_bluetooth_init(void) { new_status_color(BLACK); } #endif - if (port_boot_button_pressed()) { + if (!port_boot_button_pressed()) { + button_released = true; + } else if (button_released && !boot_in_discovery_mode) { boot_in_discovery_mode = true; - break; + // Restart the interval so the LED shows solid blue for the full + // 1000 ms, as feedback that the press was accepted and bonding + // will be erased. + start_ticks = supervisor_ticks_ms64(); } diff = supervisor_ticks_ms64() - start_ticks; } diff --git a/supervisor/shared/bluetooth/serial.c b/supervisor/shared/bluetooth/serial.c index 86ff1738a2a..f35e640e516 100644 --- a/supervisor/shared/bluetooth/serial.c +++ b/supervisor/shared/bluetooth/serial.c @@ -133,7 +133,7 @@ void supervisor_start_bluetooth_serial(void) { _common_hal_bleio_characteristic_buffer_construct(&_rx_buffer, &supervisor_ble_circuitpython_rx_characteristic, 0.1f, - (uint8_t *)_incoming, sizeof(_incoming) * sizeof(uint32_t), + (uint8_t *)_incoming, sizeof(_incoming), &rx_static_handler_entry, true /* watch for interrupt character */); diff --git a/supervisor/shared/safe_mode.c b/supervisor/shared/safe_mode.c index fade06db596..fba4c0fb37f 100644 --- a/supervisor/shared/safe_mode.c +++ b/supervisor/shared/safe_mode.c @@ -70,11 +70,13 @@ safe_mode_t wait_for_safe_mode_reset(void) { status_led_init(); #endif - uint32_t safe_mode_delay_msecs = 1000; + #define DEFAULT_SAFE_MODE_DELAY_MSECS (1000) + #define DEFAULT_SAFE_MODE_DELAY_SECS (((mp_float_t)DEFAULT_SAFE_MODE_DELAY_MSECS) / 1000.0f) + uint32_t safe_mode_delay_msecs = DEFAULT_SAFE_MODE_DELAY_MSECS; #if CIRCUITPY_SETTINGS_TOML - mp_float_t safe_mode_delay_secs = 1.0f; - // Will update delay_secs if setting is present. + mp_float_t safe_mode_delay_secs = DEFAULT_SAFE_MODE_DELAY_SECS; + // Will update safe_mode_delay_secs if setting is present. settings_get_float("CIRCUITPY_SAFE_MODE_DELAY", &safe_mode_delay_secs); if (safe_mode_delay_secs >= 0.0f && safe_mode_delay_secs <= (mp_float_t)UINT32_MAX) { safe_mode_delay_msecs = safe_mode_delay_secs * 1000; @@ -86,17 +88,20 @@ safe_mode_t wait_for_safe_mode_reset(void) { bool boot_in_safe_mode = false; while (diff < safe_mode_delay_msecs) { #if CIRCUITPY_STATUS_LED - // Blink on for 100, off for 100 - bool led_on = (diff % 250) < 125; + // Blink on for 125, off for 125 + bool led_on = boot_in_safe_mode || (diff % 250) < 125; if (led_on) { new_status_color(SAFE_MODE); } else { new_status_color(BLACK); } #endif - if (port_boot_button_pressed()) { + if (!boot_in_safe_mode && port_boot_button_pressed()) { boot_in_safe_mode = true; - break; + // Show solid yellow as feedback that the press was accepted, for the + // default duration. + start_ticks = supervisor_ticks_ms64(); + safe_mode_delay_msecs = DEFAULT_SAFE_MODE_DELAY_MSECS; } diff = supervisor_ticks_ms64() - start_ticks; } diff --git a/supervisor/shared/usb/usb_msc_flash.c b/supervisor/shared/usb/usb_msc_flash.c index 163019b7328..e9e36131c13 100644 --- a/supervisor/shared/usb/usb_msc_flash.c +++ b/supervisor/shared/usb/usb_msc_flash.c @@ -379,7 +379,8 @@ static sdcard_usb_setting_state_t _sdcard_usb_setting_state = SDCARD_USB_SETTING // Read only once to save file access time. static bool sdcard_usb_enabled(void) { if (_sdcard_usb_setting_state == SDCARD_USB_SETTING_NOT_YET_READ) { - bool setting = true; + // Can be changed per board. Most boards would leave this as true (default in circuitpy_mpconfig.h). + bool setting = CIRCUITPY_SDCARD_USB_DEFAULT; (void)settings_get_bool("CIRCUITPY_SDCARD_USB", &setting); _sdcard_usb_setting_state = setting ? SDCARD_USB_SETTING_TRUE : SDCARD_USB_SETTING_FALSE; } diff --git a/supervisor/shared/web_workflow/web_workflow.c b/supervisor/shared/web_workflow/web_workflow.c index 278823676ef..e0c9ba45095 100644 --- a/supervisor/shared/web_workflow/web_workflow.c +++ b/supervisor/shared/web_workflow/web_workflow.c @@ -398,14 +398,33 @@ bool supervisor_start_web_workflow(void) { if (common_hal_socketpool_socket_get_closed(&listening)) { #if CIRCUITPY_SOCKETPOOL_IPV6 - socketpool_socket(&pool, SOCKETPOOL_AF_INET6, SOCKETPOOL_SOCK_STREAM, 0, &listening); + bool opened = socketpool_socket(&pool, SOCKETPOOL_AF_INET6, SOCKETPOOL_SOCK_STREAM, 0, &listening); #else - socketpool_socket(&pool, SOCKETPOOL_AF_INET, SOCKETPOOL_SOCK_STREAM, 0, &listening); + bool opened = socketpool_socket(&pool, SOCKETPOOL_AF_INET, SOCKETPOOL_SOCK_STREAM, 0, &listening); #endif + // Ports with an offloaded network stack can refuse to create a + // socket until the interface has an address (this board returns + // ENOTCONN before DHCP completes). Binding and listening on the + // unset descriptor then fails with EBADF, and reporting success + // leaves the supervisor polling a socket that was never opened. + // + // Returning false avoids that but is not a retry: the background + // callback never re-enters this function, so the next attempt is + // the next supervisor_workflow_reset(), i.e. a VM restart. A board + // that only gets an address after boot will not bring the workflow + // up on its own until then. + if (!opened) { + return false; + } common_hal_socketpool_socket_settimeout(&listening, 0); - // Bind to any ip. (Not checking for failures) - common_hal_socketpool_socket_bind(&listening, "", 0, web_api_port); - common_hal_socketpool_socket_listen(&listening, 1); + if (common_hal_socketpool_socket_bind(&listening, "", 0, web_api_port) != 0) { + common_hal_socketpool_socket_close(&listening); + return false; + } + if (!common_hal_socketpool_socket_listen(&listening, 1)) { + common_hal_socketpool_socket_close(&listening); + return false; + } } // Wake polling thread (maybe) socketpool_socket_poll_resume(); @@ -884,7 +903,6 @@ static void _reply_with_devices_json(socketpool_socket_obj_t *socket, _request * "\"instance_name\": \"%s\", " "\"port\": %d, " "\"ip\": \"%d.%d.%d.%d\"}", hostname, instance_name, port, octets[0], octets[1], octets[2], octets[3]); - common_hal_mdns_remoteservice_deinit(&found_devices[i]); } #endif _send_chunk(socket, "]}"); diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index 95d651c1541..2d881dfe070 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -148,6 +148,7 @@ ifeq ($(CIRCUITPY_TINYUSB),1) shared-bindings/usb_hid/Device.c \ shared-module/usb_hid/__init__.c \ shared-module/usb_hid/Device.c \ + shared-module/usb_hid/report_descriptors.c \ endif diff --git a/tests/circuitpython/synth_note_panning.py b/tests/circuitpython/synth_note_panning.py new file mode 100644 index 00000000000..3968f2dfddb --- /dev/null +++ b/tests/circuitpython/synth_note_panning.py @@ -0,0 +1,10 @@ +from synthnotehelper import * + + +@synth_test(channel_count=2) +def gen(synth): + l = LFO(sine, offset=0.0, scale=1.0, rate=2) + yield [l] + n = Note(8, panning=l) + synth.press(n) + yield 2 diff --git a/tests/circuitpython/synth_note_panning.py.exp b/tests/circuitpython/synth_note_panning.py.exp new file mode 100644 index 00000000000..7b6bd871424 --- /dev/null +++ b/tests/circuitpython/synth_note_panning.py.exp @@ -0,0 +1,16128 @@ +0.0 0.0 0.0 0.391339111328125 +0.000125 0.003173828125 0.005218505859375 0.391339111328125 +0.00025 0.003173828125 0.005218505859375 0.391339111328125 +0.000375 0.00634765625 0.01043701171875 0.391339111328125 +0.0005 0.009552001953125 0.01568603515625 0.391339111328125 +0.000625 0.009552001953125 0.01568603515625 0.391339111328125 +0.00075 0.012725830078125 0.020904541015625 0.391339111328125 +0.000875 0.012725830078125 0.020904541015625 0.391339111328125 +0.001 0.015899658203125 0.026123046875 0.391339111328125 +0.0011250000000000002 0.019073486328125 0.0313720703125 0.391339111328125 +0.00125 0.019073486328125 0.0313720703125 0.391339111328125 +0.001375 0.022247314453125 0.036590576171875 0.391339111328125 +0.0015 0.022247314453125 0.036590576171875 0.391339111328125 +0.0016250000000000002 0.02545166015625 0.04180908203125 0.391339111328125 +0.00175 0.02862548828125 0.047027587890625 0.391339111328125 +0.001875 0.02862548828125 0.047027587890625 0.391339111328125 +0.002 0.03179931640625 0.05224609375 0.391339111328125 +0.002125 0.03179931640625 0.05224609375 0.391339111328125 +0.0022500000000000004 0.034942626953125 0.05743408203125 0.391339111328125 +0.002375 0.038116455078125 0.0626220703125 0.391339111328125 +0.0025 0.038116455078125 0.0626220703125 0.391339111328125 +0.002625 0.041259765625 0.06781005859375 0.391339111328125 +0.00275 0.041259765625 0.06781005859375 0.391339111328125 +0.002875 0.04443359375 0.072998046875 0.391339111328125 +0.003 0.047576904296875 0.07818603515625 0.391339111328125 +0.003125 0.047576904296875 0.07818603515625 0.391339111328125 +0.0032500000000000004 0.05072021484375 0.083343505859375 0.391339111328125 +0.003375 0.05072021484375 0.083343505859375 0.391339111328125 +0.0035 0.053863525390625 0.0885009765625 0.391339111328125 +0.003625 0.0570068359375 0.093658447265625 0.391339111328125 +0.00375 0.0570068359375 0.093658447265625 0.391339111328125 +0.003875 0.06011962890625 0.098785400390625 0.391339111328125 +0.004 0.06011962890625 0.098785400390625 0.391339111328125 +0.004125 0.063232421875 0.103912353515625 0.391339111328125 +0.00425 0.06634521484375 0.109039306640625 0.391339111328125 +0.004375 0.06634521484375 0.109039306640625 0.391339111328125 +0.0045000000000000006 0.0694580078125 0.1141357421875 0.391339111328125 +0.004625 0.0694580078125 0.1141357421875 0.391339111328125 +0.00475 0.07257080078125 0.119232177734375 0.391339111328125 +0.004875 0.075653076171875 0.124298095703125 0.391339111328125 +0.005 0.075653076171875 0.124298095703125 0.391339111328125 +0.005125 0.0787353515625 0.129364013671875 0.391339111328125 +0.00525 0.0787353515625 0.129364013671875 0.391339111328125 +0.0053750000000000008 0.081817626953125 0.134429931640625 0.391339111328125 +0.0055 0.084869384765625 0.13946533203125 0.391339111328125 +0.005625 0.084869384765625 0.13946533203125 0.391339111328125 +0.00575 0.087921142578125 0.14447021484375 0.391339111328125 +0.005875 0.087921142578125 0.14447021484375 0.391339111328125 +0.006 0.090972900390625 0.14947509765625 0.391339111328125 +0.006125 0.093994140625 0.15447998046875 0.391339111328125 +0.00625 0.093994140625 0.15447998046875 0.391339111328125 +0.0063750000000000008 0.0970458984375 0.159454345703125 0.391339111328125 +0.006500000000000001 0.0970458984375 0.159454345703125 0.391339111328125 +0.006625 0.10003662109375 0.164398193359375 0.391339111328125 +0.00675 0.103057861328125 0.169342041015625 0.391339111328125 +0.006875 0.103057861328125 0.169342041015625 0.391339111328125 +0.007 0.106048583984375 0.17425537109375 0.391339111328125 +0.007125 0.106048583984375 0.17425537109375 0.391339111328125 +0.00725 0.1090087890625 0.17913818359375 0.391339111328125 +0.00737500000000000064 0.11199951171875 0.18402099609375 0.391339111328125 +0.0075 0.11199951171875 0.18402099609375 0.391339111328125 +0.007625 0.114959716796875 0.188873291015625 0.391339111328125 +0.00775 0.114959716796875 0.188873291015625 0.391339111328125 +0.007875 0.117889404296875 0.1937255859375 0.391339111328125 +0.008 0.120819091796875 0.19854736328125 0.391339111328125 +0.008125 0.120819091796875 0.19854736328125 0.391339111328125 +0.00825 0.123748779296875 0.203338623046875 0.391339111328125 +0.008375 0.123748779296875 0.203338623046875 0.391339111328125 +0.008500000000000001 0.12664794921875 0.208099365234375 0.391339111328125 +0.008625000000000001 0.129547119140625 0.212860107421875 0.391339111328125 +0.008750000000000001 0.129547119140625 0.212860107421875 0.391339111328125 +0.008875000000000001 0.132415771484375 0.21759033203125 0.391339111328125 +0.009000000000000001 0.132415771484375 0.21759033203125 0.391339111328125 +0.009125 0.135284423828125 0.2222900390625 0.391339111328125 +0.00925 0.13812255859375 0.226959228515625 0.391339111328125 +0.009375 0.13812255859375 0.226959228515625 0.391339111328125 +0.0095 0.140960693359375 0.231597900390625 0.391339111328125 +0.009625 0.140960693359375 0.231597900390625 0.391339111328125 +0.00975 0.143768310546875 0.236236572265625 0.391339111328125 +0.009875 0.146575927734375 0.2408447265625 0.391339111328125 +0.01 0.146575927734375 0.2408447265625 0.391339111328125 +0.010125 0.14935302734375 0.24542236328125 0.391339111328125 +0.01025 0.14935302734375 0.24542236328125 0.391339111328125 +0.010375 0.152130126953125 0.249969482421875 0.391339111328125 +0.0105 0.154876708984375 0.25445556640625 0.391339111328125 +0.010625 0.154876708984375 0.25445556640625 0.391339111328125 +0.0107500000000000016 0.157623291015625 0.25897216796875 0.391339111328125 +0.0108750000000000016 0.157623291015625 0.25897216796875 0.391339111328125 +0.011 0.160308837890625 0.263427734375 0.391339111328125 +0.011125 0.16302490234375 0.267852783203125 0.391339111328125 +0.01125 0.16302490234375 0.267852783203125 0.391339111328125 +0.011375 0.16571044921875 0.27227783203125 0.391339111328125 +0.0115 0.16571044921875 0.27227783203125 0.391339111328125 +0.011625 0.168365478515625 0.276641845703125 0.391339111328125 +0.01175 0.1710205078125 0.280975341796875 0.391339111328125 +0.011875 0.1710205078125 0.280975341796875 0.391339111328125 +0.012 0.17364501953125 0.285308837890625 0.391339111328125 +0.012125 0.17364501953125 0.285308837890625 0.391339111328125 +0.01225 0.176239013671875 0.289581298828125 0.391339111328125 +0.012375 0.1788330078125 0.2938232421875 0.391339111328125 +0.0125 0.1788330078125 0.2938232421875 0.391339111328125 +0.012625 0.181396484375 0.298065185546875 0.391339111328125 +0.0127500000000000016 0.181396484375 0.298065185546875 0.391339111328125 +0.0128750000000000016 0.1839599609375 0.30224609375 0.391339111328125 +0.013000000000000002 0.186492919921875 0.306396484375 0.391339111328125 +0.013125 0.186492919921875 0.306396484375 0.391339111328125 +0.01325 0.188995361328125 0.310516357421875 0.391339111328125 +0.013375 0.188995361328125 0.310516357421875 0.391339111328125 +0.0135 0.19146728515625 0.314605712890625 0.391339111328125 +0.013625 0.193939208984375 0.31866455078125 0.391339111328125 +0.01375 0.193939208984375 0.31866455078125 0.391339111328125 +0.013875 0.196380615234375 0.322662353515625 0.391339111328125 +0.014 0.196380615234375 0.322662353515625 0.391339111328125 +0.014125 0.19879150390625 0.32666015625 0.391339111328125 +0.01425 0.201202392578125 0.330596923828125 0.391339111328125 +0.014375 0.201202392578125 0.330596923828125 0.391339111328125 +0.0145 0.203582763671875 0.334503173828125 0.391339111328125 +0.014625 0.203582763671875 0.334503173828125 0.391339111328125 +0.0147500000000000016 0.2059326171875 0.33837890625 0.391339111328125 +0.0148750000000000013 0.208282470703125 0.34222412109375 0.391339111328125 +0.015 0.208282470703125 0.34222412109375 0.391339111328125 +0.015125 0.210601806640625 0.34600830078125 0.391339111328125 +0.01525 0.210601806640625 0.34600830078125 0.391339111328125 +0.015375 0.212860107421875 0.349761962890625 0.391339111328125 +0.0155 0.21514892578125 0.353485107421875 0.391339111328125 +0.015625 0.21514892578125 0.353485107421875 0.391339111328125 +0.01575 0.217376708984375 0.357177734375 0.391339111328125 +0.015875 0.217376708984375 0.357177734375 0.391339111328125 +0.016 0.2196044921875 0.360809326171875 0.391339111328125 +0.016125 0.2218017578125 0.36444091796875 0.391339111328125 +0.01625 0.2218017578125 0.36444091796875 0.391339111328125 +0.016375 0.223968505859375 0.36798095703125 0.391339111328125 +0.0165 0.223968505859375 0.36798095703125 0.391339111328125 +0.016625 0.226104736328125 0.37152099609375 0.391339111328125 +0.01675 0.22821044921875 0.375 0.391339111328125 +0.016875 0.22821044921875 0.375 0.391339111328125 +0.017 0.230316162109375 0.378448486328125 0.391339111328125 +0.017125 0.230316162109375 0.378448486328125 0.391339111328125 +0.01725 0.232391357421875 0.3818359375 0.391339111328125 +0.017375 0.23443603515625 0.38519287109375 0.391339111328125 +0.0175 0.23443603515625 0.38519287109375 0.391339111328125 +0.017625000000000002 0.2364501953125 0.388519287109375 0.391339111328125 +0.017750000000000002 0.2364501953125 0.388519287109375 0.391339111328125 +0.017875000000000002 0.238433837890625 0.39178466796875 0.391339111328125 +0.018000000000000002 0.24041748046875 0.39501953125 0.391339111328125 +0.018125 0.24041748046875 0.39501953125 0.391339111328125 +0.01825 0.242340087890625 0.398193359375 0.391339111328125 +0.018375 0.242340087890625 0.398193359375 0.391339111328125 +0.0185 0.2442626953125 0.401336669921875 0.391339111328125 +0.018625 0.24615478515625 0.404449462890625 0.391339111328125 +0.01875 0.24615478515625 0.404449462890625 0.391339111328125 +0.018875 0.248016357421875 0.407501220703125 0.391339111328125 +0.019 0.248016357421875 0.407501220703125 0.391339111328125 +0.019125 0.249847412109375 0.4105224609375 0.391339111328125 +0.01925 0.25164794921875 0.413482666015625 0.391339111328125 +0.019375 0.25164794921875 0.413482666015625 0.391339111328125 +0.0195 0.25341796875 0.416412353515625 0.391339111328125 +0.019625 0.25341796875 0.416412353515625 0.391339111328125 +0.01975 0.255157470703125 0.419281005859375 0.391339111328125 +0.019875 0.25689697265625 0.422119140625 0.391339111328125 +0.02 0.25689697265625 0.422119140625 0.391339111328125 +0.020125 0.258575439453125 0.424896240234375 0.391339111328125 +0.02025 0.258575439453125 0.424896240234375 0.391339111328125 +0.020375 0.26025390625 0.4276123046875 0.391339111328125 +0.0205 0.261871337890625 0.4302978515625 0.391339111328125 +0.020625 0.261871337890625 0.4302978515625 0.391339111328125 +0.02075 0.26348876953125 0.432952880859375 0.391339111328125 +0.020875 0.26348876953125 0.432952880859375 0.391339111328125 +0.021 0.26507568359375 0.435546875 0.391339111328125 +0.021125 0.266632080078125 0.438079833984375 0.391339111328125 +0.02125 0.266632080078125 0.438079833984375 0.391339111328125 +0.021375 0.268157958984375 0.440582275390625 0.391339111328125 +0.0215000000000000032 0.268157958984375 0.440582275390625 0.391339111328125 +0.0216250000000000032 0.269622802734375 0.44305419921875 0.391339111328125 +0.0217500000000000032 0.271087646484375 0.4454345703125 0.391339111328125 +0.0218750000000000032 0.271087646484375 0.4454345703125 0.391339111328125 +0.022 0.27252197265625 0.447784423828125 0.391339111328125 +0.022125 0.27252197265625 0.447784423828125 0.391339111328125 +0.02225 0.27392578125 0.450103759765625 0.391339111328125 +0.022375 0.275299072265625 0.452362060546875 0.391339111328125 +0.0225 0.275299072265625 0.452362060546875 0.391339111328125 +0.022625 0.276641845703125 0.454559326171875 0.391339111328125 +0.02275 0.276641845703125 0.454559326171875 0.391339111328125 +0.022875 0.2779541015625 0.45672607421875 0.391339111328125 +0.023 0.27923583984375 0.458831787109375 0.391339111328125 +0.023125 0.27923583984375 0.458831787109375 0.391339111328125 +0.02325 0.280487060546875 0.46087646484375 0.391339111328125 +0.023375 0.280487060546875 0.46087646484375 0.391339111328125 +0.0235 0.281707763671875 0.462890625 0.391339111328125 +0.023625 0.28289794921875 0.464813232421875 0.391339111328125 +0.02375 0.28289794921875 0.464813232421875 0.391339111328125 +0.023875 0.2840576171875 0.46673583984375 0.391339111328125 +0.024 0.2840576171875 0.46673583984375 0.391339111328125 +0.024125 0.285186767578125 0.46856689453125 0.391339111328125 +0.02425 0.2862548828125 0.470367431640625 0.391339111328125 +0.024375 0.2862548828125 0.470367431640625 0.391339111328125 +0.0245 0.287322998046875 0.472137451171875 0.391339111328125 +0.024625 0.287322998046875 0.472137451171875 0.391339111328125 +0.02475 0.288360595703125 0.47381591796875 0.391339111328125 +0.024875 0.28936767578125 0.4754638671875 0.391339111328125 +0.025 0.28936767578125 0.4754638671875 0.391339111328125 +0.025125 0.29034423828125 0.47705078125 0.391339111328125 +0.02525 0.29034423828125 0.47705078125 0.391339111328125 +0.025375 0.291259765625 0.478607177734375 0.391339111328125 +0.0255000000000000032 0.29217529296875 0.480072021484375 0.391339111328125 +0.0256250000000000032 0.29217529296875 0.480072021484375 0.391339111328125 +0.0257500000000000032 0.293060302734375 0.48150634765625 0.391339111328125 +0.0258750000000000032 0.293060302734375 0.48150634765625 0.391339111328125 +0.026000000000000004 0.29388427734375 0.48291015625 0.391339111328125 +0.026125 0.294708251953125 0.484222412109375 0.391339111328125 +0.02625 0.294708251953125 0.484222412109375 0.391339111328125 +0.026375 0.29547119140625 0.485504150390625 0.391339111328125 +0.0265 0.29547119140625 0.485504150390625 0.391339111328125 +0.026625 0.296234130859375 0.486724853515625 0.391339111328125 +0.02675 0.29693603515625 0.487884521484375 0.391339111328125 +0.026875 0.29693603515625 0.487884521484375 0.391339111328125 +0.027 0.297607421875 0.489013671875 0.391339111328125 +0.027125 0.297607421875 0.489013671875 0.391339111328125 +0.02725 0.298248291015625 0.490081787109375 0.391339111328125 +0.027375 0.298858642578125 0.4910888671875 0.391339111328125 +0.0275 0.298858642578125 0.4910888671875 0.391339111328125 +0.027625 0.299468994140625 0.492034912109375 0.391339111328125 +0.02775 0.299468994140625 0.492034912109375 0.391339111328125 +0.027875 0.300018310546875 0.492950439453125 0.391339111328125 +0.028 0.300506591796875 0.4937744140625 0.391339111328125 +0.028125 0.300506591796875 0.4937744140625 0.391339111328125 +0.02825 0.300994873046875 0.49456787109375 0.391339111328125 +0.028375 0.300994873046875 0.49456787109375 0.391339111328125 +0.0285 0.30145263671875 0.49530029296875 0.391339111328125 +0.028625 0.301849365234375 0.496002197265625 0.391339111328125 +0.02875 0.301849365234375 0.496002197265625 0.391339111328125 +0.028875 0.30224609375 0.496612548828125 0.391339111328125 +0.029 0.30224609375 0.496612548828125 0.391339111328125 +0.029125 0.302581787109375 0.4971923828125 0.391339111328125 +0.02925 0.30291748046875 0.497711181640625 0.391339111328125 +0.0293750000000000032 0.30291748046875 0.497711181640625 0.391339111328125 +0.0295000000000000032 0.303192138671875 0.498199462890625 0.391339111328125 +0.0296250000000000026 0.303192138671875 0.498199462890625 0.391339111328125 +0.0297500000000000026 0.303436279296875 0.49859619140625 0.391339111328125 +0.0298750000000000026 0.30364990234375 0.49896240234375 0.391339111328125 +0.03 0.30364990234375 0.49896240234375 0.391339111328125 +0.030125 0.303863525390625 0.499267578125 0.391339111328125 +0.03025 0.303863525390625 0.499267578125 0.391339111328125 +0.030375 0.303985595703125 0.49951171875 0.391339111328125 +0.0305 0.304107666015625 0.49969482421875 0.391339111328125 +0.030625 0.304107666015625 0.49969482421875 0.391339111328125 +0.03075 0.30419921875 0.49981689453125 0.391339111328125 +0.030875 0.30419921875 0.49981689453125 0.391339111328125 +0.031 0.30426025390625 0.499908447265625 0.391339111328125 +0.031125 0.30426025390625 0.49993896484375 0.391339111328125 +0.03125 0.30426025390625 0.49993896484375 0.391339111328125 +0.031375 0.30426025390625 0.499908447265625 0.391339111328125 +0.0315 0.30426025390625 0.499908447265625 0.391339111328125 +0.031625 0.30419921875 0.49981689453125 0.391339111328125 +0.03175 0.304107666015625 0.49969482421875 0.391339111328125 +0.031875 0.304107666015625 0.49969482421875 0.391339111328125 +0.032 0.139678955078125 0.49951171875 0.7202575683593749 +0.032125 0.139678955078125 0.49951171875 0.7202575683593749 +0.03225 0.139617919921875 0.499267578125 0.7202575683593749 +0.032375 0.1395263671875 0.49896240234375 0.7202575683593749 +0.0325 0.1395263671875 0.49896240234375 0.7202575683593749 +0.032625 0.139434814453125 0.49859619140625 0.7202575683593749 +0.03275 0.139434814453125 0.49859619140625 0.7202575683593749 +0.032875 0.139312744140625 0.498199462890625 0.7202575683593749 +0.033 0.139190673828125 0.497711181640625 0.7202575683593749 +0.033125 0.139190673828125 0.497711181640625 0.7202575683593749 +0.03325 0.1390380859375 0.4971923828125 0.7202575683593749 +0.033375 0.1390380859375 0.4971923828125 0.7202575683593749 +0.0335 0.138885498046875 0.496612548828125 0.7202575683593749 +0.033625 0.138702392578125 0.496002197265625 0.7202575683593749 +0.03375 0.138702392578125 0.496002197265625 0.7202575683593749 +0.033875 0.138519287109375 0.49530029296875 0.7202575683593749 +0.034 0.138519287109375 0.49530029296875 0.7202575683593749 +0.034125 0.1383056640625 0.49456787109375 0.7202575683593749 +0.03425 0.138092041015625 0.4937744140625 0.7202575683593749 +0.034375 0.138092041015625 0.4937744140625 0.7202575683593749 +0.0345 0.137847900390625 0.492950439453125 0.7202575683593749 +0.034625 0.137847900390625 0.492950439453125 0.7202575683593749 +0.03475 0.137603759765625 0.492034912109375 0.7202575683593749 +0.034875 0.1373291015625 0.4910888671875 0.7202575683593749 +0.035 0.1373291015625 0.4910888671875 0.7202575683593749 +0.035125 0.137054443359375 0.490081787109375 0.7202575683593749 +0.035250000000000004 0.137054443359375 0.490081787109375 0.7202575683593749 +0.035375000000000004 0.136749267578125 0.489013671875 0.7202575683593749 +0.035500000000000004 0.136444091796875 0.487884521484375 0.7202575683593749 +0.035625000000000004 0.136444091796875 0.487884521484375 0.7202575683593749 +0.035750000000000004 0.1361083984375 0.486724853515625 0.7202575683593749 +0.035875000000000004 0.1361083984375 0.486724853515625 0.7202575683593749 +0.036000000000000004 0.135772705078125 0.485504150390625 0.7202575683593749 +0.036125 0.135406494140625 0.484222412109375 0.7202575683593749 +0.03625 0.135406494140625 0.484222412109375 0.7202575683593749 +0.036375 0.135040283203125 0.48291015625 0.7202575683593749 +0.0365 0.135040283203125 0.48291015625 0.7202575683593749 +0.036625 0.1346435546875 0.48150634765625 0.7202575683593749 +0.03675 0.134246826171875 0.480072021484375 0.7202575683593749 +0.036875 0.134246826171875 0.480072021484375 0.7202575683593749 +0.037 0.13385009765625 0.478607177734375 0.7202575683593749 +0.037125 0.13385009765625 0.478607177734375 0.7202575683593749 +0.03725 0.1334228515625 0.47705078125 0.7202575683593749 +0.037375 0.132965087890625 0.4754638671875 0.7202575683593749 +0.0375 0.132965087890625 0.4754638671875 0.7202575683593749 +0.037625 0.13250732421875 0.47381591796875 0.7202575683593749 +0.03775 0.13250732421875 0.47381591796875 0.7202575683593749 +0.037875 0.13201904296875 0.472137451171875 0.7202575683593749 +0.038 0.13153076171875 0.470367431640625 0.7202575683593749 +0.038125 0.13153076171875 0.470367431640625 0.7202575683593749 +0.03825 0.13104248046875 0.46856689453125 0.7202575683593749 +0.038375 0.13104248046875 0.46856689453125 0.7202575683593749 +0.0385 0.130523681640625 0.46673583984375 0.7202575683593749 +0.038625 0.129974365234375 0.464813232421875 0.7202575683593749 +0.03875 0.129974365234375 0.464813232421875 0.7202575683593749 +0.038875 0.12945556640625 0.462890625 0.7202575683593749 +0.039 0.12945556640625 0.462890625 0.7202575683593749 +0.039125 0.128875732421875 0.46087646484375 0.7202575683593749 +0.03925 0.128326416015625 0.458831787109375 0.7202575683593749 +0.039375 0.128326416015625 0.458831787109375 0.7202575683593749 +0.0395 0.127716064453125 0.45672607421875 0.7202575683593749 +0.039625 0.127716064453125 0.45672607421875 0.7202575683593749 +0.03975 0.127105712890625 0.454559326171875 0.7202575683593749 +0.039875 0.126495361328125 0.452362060546875 0.7202575683593749 +0.04 0.126495361328125 0.452362060546875 0.7202575683593749 +0.040125 0.125885009765625 0.450103759765625 0.7202575683593749 +0.04025 0.125885009765625 0.450103759765625 0.7202575683593749 +0.040375 0.125213623046875 0.447784423828125 0.7202575683593749 +0.0405 0.12457275390625 0.4454345703125 0.7202575683593749 +0.040625 0.12457275390625 0.4454345703125 0.7202575683593749 +0.04075 0.1239013671875 0.44305419921875 0.7202575683593749 +0.040875 0.1239013671875 0.44305419921875 0.7202575683593749 +0.041 0.123199462890625 0.440582275390625 0.7202575683593749 +0.041125 0.12249755859375 0.438079833984375 0.7202575683593749 +0.04125 0.12249755859375 0.438079833984375 0.7202575683593749 +0.041375 0.121795654296875 0.435546875 0.7202575683593749 +0.0415 0.121795654296875 0.435546875 0.7202575683593749 +0.041625 0.121063232421875 0.432952880859375 0.7202575683593749 +0.04175 0.120330810546875 0.4302978515625 0.7202575683593749 +0.041875 0.120330810546875 0.4302978515625 0.7202575683593749 +0.042 0.119598388671875 0.4276123046875 0.7202575683593749 +0.042125 0.119598388671875 0.4276123046875 0.7202575683593749 +0.04225 0.11883544921875 0.424896240234375 0.7202575683593749 +0.042375 0.1180419921875 0.422119140625 0.7202575683593749 +0.0425 0.1180419921875 0.422119140625 0.7202575683593749 +0.042625 0.11724853515625 0.419281005859375 0.7202575683593749 +0.04275 0.11724853515625 0.419281005859375 0.7202575683593749 +0.042875 0.116455078125 0.416412353515625 0.7202575683593749 +0.0430000000000000036 0.115631103515625 0.413482666015625 0.7202575683593749 +0.0431250000000000036 0.115631103515625 0.413482666015625 0.7202575683593749 +0.0432500000000000036 0.11480712890625 0.4105224609375 0.7202575683593749 +0.0433750000000000036 0.11480712890625 0.4105224609375 0.7202575683593749 +0.0435000000000000036 0.11395263671875 0.407501220703125 0.7202575683593749 +0.0436250000000000036 0.11309814453125 0.404449462890625 0.7202575683593749 +0.0437500000000000036 0.11309814453125 0.404449462890625 0.7202575683593749 +0.0438750000000000036 0.11224365234375 0.401336669921875 0.7202575683593749 +0.044 0.11224365234375 0.401336669921875 0.7202575683593749 +0.044125 0.111358642578125 0.398193359375 0.7202575683593749 +0.04425 0.1104736328125 0.39501953125 0.7202575683593749 +0.044375 0.1104736328125 0.39501953125 0.7202575683593749 +0.0445 0.10955810546875 0.39178466796875 0.7202575683593749 +0.044625 0.10955810546875 0.39178466796875 0.7202575683593749 +0.04475 0.108642578125 0.388519287109375 0.7202575683593749 +0.044875 0.10772705078125 0.38519287109375 0.7202575683593749 +0.045 0.10772705078125 0.38519287109375 0.7202575683593749 +0.045125 0.106781005859375 0.3818359375 0.7202575683593749 +0.04525 0.106781005859375 0.3818359375 0.7202575683593749 +0.045375 0.1058349609375 0.378448486328125 0.7202575683593749 +0.0455 0.1048583984375 0.375 0.7202575683593749 +0.045625 0.1048583984375 0.375 0.7202575683593749 +0.04575 0.1038818359375 0.37152099609375 0.7202575683593749 +0.045875 0.1038818359375 0.37152099609375 0.7202575683593749 +0.046 0.1029052734375 0.36798095703125 0.7202575683593749 +0.046125 0.1019287109375 0.36444091796875 0.7202575683593749 +0.04625 0.1019287109375 0.36444091796875 0.7202575683593749 +0.046375 0.10089111328125 0.360809326171875 0.7202575683593749 +0.0465 0.10089111328125 0.360809326171875 0.7202575683593749 +0.046625 0.099884033203125 0.357177734375 0.7202575683593749 +0.04675 0.098846435546875 0.353485107421875 0.7202575683593749 +0.046875 0.098846435546875 0.353485107421875 0.7202575683593749 +0.047 0.097808837890625 0.349761962890625 0.7202575683593749 +0.047125 0.097808837890625 0.349761962890625 0.7202575683593749 +0.04725 0.096771240234375 0.34600830078125 0.7202575683593749 +0.047375 0.095703125 0.34222412109375 0.7202575683593749 +0.0475 0.095703125 0.34222412109375 0.7202575683593749 +0.047625 0.094635009765625 0.33837890625 0.7202575683593749 +0.04775 0.094635009765625 0.33837890625 0.7202575683593749 +0.047875 0.093536376953125 0.334503173828125 0.7202575683593749 +0.048 0.09246826171875 0.330596923828125 0.7202575683593749 +0.048125 0.09246826171875 0.330596923828125 0.7202575683593749 +0.04825 0.091339111328125 0.32666015625 0.7202575683593749 +0.048375 0.091339111328125 0.32666015625 0.7202575683593749 +0.0485 0.090240478515625 0.322662353515625 0.7202575683593749 +0.048625 0.089111328125 0.31866455078125 0.7202575683593749 +0.04875 0.089111328125 0.31866455078125 0.7202575683593749 +0.048875 0.087982177734375 0.314605712890625 0.7202575683593749 +0.049 0.087982177734375 0.314605712890625 0.7202575683593749 +0.049125 0.086822509765625 0.310516357421875 0.7202575683593749 +0.04925 0.085693359375 0.306396484375 0.7202575683593749 +0.049375 0.085693359375 0.306396484375 0.7202575683593749 +0.0495 0.08453369140625 0.30224609375 0.7202575683593749 +0.049625 0.08453369140625 0.30224609375 0.7202575683593749 +0.04975 0.083343505859375 0.298065185546875 0.7202575683593749 +0.049875 0.082183837890625 0.2938232421875 0.7202575683593749 +0.05 0.082183837890625 0.2938232421875 0.7202575683593749 +0.050125 0.08099365234375 0.289581298828125 0.7202575683593749 +0.05025 0.08099365234375 0.289581298828125 0.7202575683593749 +0.050375 0.07977294921875 0.285308837890625 0.7202575683593749 +0.0505 0.078582763671875 0.280975341796875 0.7202575683593749 +0.050625 0.078582763671875 0.280975341796875 0.7202575683593749 +0.05075 0.077362060546875 0.276641845703125 0.7202575683593749 +0.0508750000000000064 0.077362060546875 0.276641845703125 0.7202575683593749 +0.0510000000000000064 0.076141357421875 0.27227783203125 0.7202575683593749 +0.0511250000000000064 0.074920654296875 0.267852783203125 0.7202575683593749 +0.0512500000000000064 0.074920654296875 0.267852783203125 0.7202575683593749 +0.0513750000000000064 0.07366943359375 0.263427734375 0.7202575683593749 +0.0515000000000000064 0.07366943359375 0.263427734375 0.7202575683593749 +0.0516250000000000064 0.072418212890625 0.25897216796875 0.7202575683593749 +0.0517500000000000064 0.0711669921875 0.25445556640625 0.7202575683593749 +0.0518750000000000064 0.0711669921875 0.25445556640625 0.7202575683593749 +0.052000000000000008 0.06988525390625 0.249969482421875 0.7202575683593749 +0.052125 0.06988525390625 0.249969482421875 0.7202575683593749 +0.05225 0.068634033203125 0.24542236328125 0.7202575683593749 +0.052375 0.067352294921875 0.2408447265625 0.7202575683593749 +0.0525 0.067352294921875 0.2408447265625 0.7202575683593749 +0.052625 0.066070556640625 0.236236572265625 0.7202575683593749 +0.05275 0.066070556640625 0.236236572265625 0.7202575683593749 +0.052875 0.06475830078125 0.231597900390625 0.7202575683593749 +0.053 0.063446044921875 0.226959228515625 0.7202575683593749 +0.053125 0.063446044921875 0.226959228515625 0.7202575683593749 +0.05325 0.062164306640625 0.2222900390625 0.7202575683593749 +0.053375 0.062164306640625 0.2222900390625 0.7202575683593749 +0.0535 0.06085205078125 0.21759033203125 0.7202575683593749 +0.053625 0.05950927734375 0.212860107421875 0.7202575683593749 +0.05375 0.05950927734375 0.212860107421875 0.7202575683593749 +0.053875 0.058197021484375 0.208099365234375 0.7202575683593749 +0.054 0.058197021484375 0.208099365234375 0.7202575683593749 +0.054125 0.056854248046875 0.203338623046875 0.7202575683593749 +0.05425 0.055511474609375 0.19854736328125 0.7202575683593749 +0.054375 0.055511474609375 0.19854736328125 0.7202575683593749 +0.0545 0.054168701171875 0.1937255859375 0.7202575683593749 +0.054625 0.054168701171875 0.1937255859375 0.7202575683593749 +0.05475 0.052825927734375 0.188873291015625 0.7202575683593749 +0.054875 0.05145263671875 0.18402099609375 0.7202575683593749 +0.055 0.05145263671875 0.18402099609375 0.7202575683593749 +0.055125 0.050079345703125 0.17913818359375 0.7202575683593749 +0.05525 0.050079345703125 0.17913818359375 0.7202575683593749 +0.055375 0.048736572265625 0.17425537109375 0.7202575683593749 +0.0555 0.047332763671875 0.169342041015625 0.7202575683593749 +0.055625 0.047332763671875 0.169342041015625 0.7202575683593749 +0.05575 0.04595947265625 0.164398193359375 0.7202575683593749 +0.055875 0.04595947265625 0.164398193359375 0.7202575683593749 +0.056 0.044586181640625 0.159454345703125 0.7202575683593749 +0.056125 0.043182373046875 0.15447998046875 0.7202575683593749 +0.05625 0.043182373046875 0.15447998046875 0.7202575683593749 +0.056375 0.04180908203125 0.14947509765625 0.7202575683593749 +0.0565 0.04180908203125 0.14947509765625 0.7202575683593749 +0.056625 0.0404052734375 0.14447021484375 0.7202575683593749 +0.05675 0.03900146484375 0.13946533203125 0.7202575683593749 +0.056875 0.03900146484375 0.13946533203125 0.7202575683593749 +0.057 0.03759765625 0.134429931640625 0.7202575683593749 +0.057125 0.03759765625 0.134429931640625 0.7202575683593749 +0.05725 0.036163330078125 0.129364013671875 0.7202575683593749 +0.057375 0.034759521484375 0.124298095703125 0.7202575683593749 +0.0575 0.034759521484375 0.124298095703125 0.7202575683593749 +0.057625 0.0333251953125 0.119232177734375 0.7202575683593749 +0.05775 0.0333251953125 0.119232177734375 0.7202575683593749 +0.057875 0.03192138671875 0.1141357421875 0.7202575683593749 +0.058 0.030487060546875 0.109039306640625 0.7202575683593749 +0.058125 0.030487060546875 0.109039306640625 0.7202575683593749 +0.05825 0.029052734375 0.103912353515625 0.7202575683593749 +0.058375 0.029052734375 0.103912353515625 0.7202575683593749 +0.0585 0.027618408203125 0.098785400390625 0.7202575683593749 +0.0586250000000000064 0.02618408203125 0.093658447265625 0.7202575683593749 +0.0587500000000000064 0.02618408203125 0.093658447265625 0.7202575683593749 +0.0588750000000000064 0.024749755859375 0.0885009765625 0.7202575683593749 +0.0590000000000000064 0.024749755859375 0.0885009765625 0.7202575683593749 +0.0591250000000000064 0.0233154296875 0.083343505859375 0.7202575683593749 +0.0592500000000000052 0.0218505859375 0.07818603515625 0.7202575683593749 +0.0593750000000000052 0.0218505859375 0.07818603515625 0.7202575683593749 +0.0595000000000000052 0.020416259765625 0.072998046875 0.7202575683593749 +0.0596250000000000052 0.020416259765625 0.072998046875 0.7202575683593749 +0.0597500000000000052 0.018951416015625 0.06781005859375 0.7202575683593749 +0.059875000000000008 0.01751708984375 0.0626220703125 0.7202575683593749 +0.06 0.01751708984375 0.0626220703125 0.7202575683593749 +0.060125 0.01605224609375 0.05743408203125 0.7202575683593749 +0.06025 0.01605224609375 0.05743408203125 0.7202575683593749 +0.060375 0.01458740234375 0.05224609375 0.7202575683593749 +0.0605 0.013153076171875 0.047027587890625 0.7202575683593749 +0.060625 0.013153076171875 0.047027587890625 0.7202575683593749 +0.06075 0.011688232421875 0.04180908203125 0.7202575683593749 +0.060875 0.011688232421875 0.04180908203125 0.7202575683593749 +0.061 0.010223388671875 0.036590576171875 0.7202575683593749 +0.061125 0.008758544921875 0.0313720703125 0.7202575683593749 +0.06125 0.008758544921875 0.0313720703125 0.7202575683593749 +0.061375 0.007293701171875 0.026123046875 0.7202575683593749 +0.0615 0.007293701171875 0.026123046875 0.7202575683593749 +0.061625 0.005828857421875 0.020904541015625 0.7202575683593749 +0.06175 0.004364013671875 0.01568603515625 0.7202575683593749 +0.061875 0.004364013671875 0.01568603515625 0.7202575683593749 +0.062 0.002899169921875 0.01043701171875 0.7202575683593749 +0.062125 0.002899169921875 0.01043701171875 0.7202575683593749 +0.06225 0.001434326171875 0.005218505859375 0.7202575683593749 +0.062375 0.0 0.0 0.7202575683593749 +0.0625 0.0 0.0 0.7202575683593749 +0.062625 -0.001434326171875 -0.005218505859375 0.7202575683593749 +0.06275 -0.001434326171875 -0.005218505859375 0.7202575683593749 +0.062875 -0.002899169921875 -0.01043701171875 0.7202575683593749 +0.063 -0.004364013671875 -0.01568603515625 0.7202575683593749 +0.063125 -0.004364013671875 -0.01568603515625 0.7202575683593749 +0.06325 -0.005828857421875 -0.020904541015625 0.7202575683593749 +0.063375 -0.005828857421875 -0.020904541015625 0.7202575683593749 +0.0635 -0.007293701171875 -0.026123046875 0.7202575683593749 +0.063625 -0.008758544921875 -0.0313720703125 0.7202575683593749 +0.06375 -0.008758544921875 -0.0313720703125 0.7202575683593749 +0.063875 -0.010223388671875 -0.036590576171875 0.7202575683593749 +0.064 -0.00238037109375 -0.036590576171875 0.934271240234375 +0.064125 -0.002716064453125 -0.04180908203125 0.934271240234375 +0.06425 -0.003082275390625 -0.047027587890625 0.934271240234375 +0.064375 -0.003082275390625 -0.047027587890625 0.934271240234375 +0.0645 -0.00341796875 -0.05224609375 0.934271240234375 +0.064625 -0.00341796875 -0.05224609375 0.934271240234375 +0.06475 -0.003753662109375 -0.05743408203125 0.934271240234375 +0.064875 -0.00408935546875 -0.0626220703125 0.934271240234375 +0.065 -0.00408935546875 -0.0626220703125 0.934271240234375 +0.065125 -0.004425048828125 -0.06781005859375 0.934271240234375 +0.06525 -0.004425048828125 -0.06781005859375 0.934271240234375 +0.065375 -0.004791259765625 -0.072998046875 0.934271240234375 +0.0655 -0.005126953125 -0.07818603515625 0.934271240234375 +0.065625 -0.005126953125 -0.07818603515625 0.934271240234375 +0.06575 -0.005462646484375 -0.083343505859375 0.934271240234375 +0.065875 -0.005462646484375 -0.083343505859375 0.934271240234375 +0.066 -0.00579833984375 -0.0885009765625 0.934271240234375 +0.066125 -0.006134033203125 -0.093658447265625 0.934271240234375 +0.06625 -0.006134033203125 -0.093658447265625 0.934271240234375 +0.066375 -0.0064697265625 -0.098785400390625 0.934271240234375 +0.0665 -0.0064697265625 -0.098785400390625 0.934271240234375 +0.066625 -0.006805419921875 -0.103912353515625 0.934271240234375 +0.06675 -0.00714111328125 -0.109039306640625 0.934271240234375 +0.066875 -0.00714111328125 -0.109039306640625 0.934271240234375 +0.067 -0.007476806640625 -0.1141357421875 0.934271240234375 +0.067125 -0.007476806640625 -0.1141357421875 0.934271240234375 +0.06725 -0.0078125 -0.119232177734375 0.934271240234375 +0.067375 -0.008148193359375 -0.124298095703125 0.934271240234375 +0.0675 -0.008148193359375 -0.124298095703125 0.934271240234375 +0.067625 -0.00848388671875 -0.129364013671875 0.934271240234375 +0.06775 -0.00848388671875 -0.129364013671875 0.934271240234375 +0.067875 -0.008819580078125 -0.134429931640625 0.934271240234375 +0.068 -0.0091552734375 -0.13946533203125 0.934271240234375 +0.068125 -0.0091552734375 -0.13946533203125 0.934271240234375 +0.06825000000000001 -0.00946044921875 -0.14447021484375 0.934271240234375 +0.06837500000000001 -0.00946044921875 -0.14447021484375 0.934271240234375 +0.06850000000000001 -0.009796142578125 -0.14947509765625 0.934271240234375 +0.06862500000000001 -0.0101318359375 -0.15447998046875 0.934271240234375 +0.06875000000000001 -0.0101318359375 -0.15447998046875 0.934271240234375 +0.06887500000000001 -0.010467529296875 -0.159454345703125 0.934271240234375 +0.06900000000000001 -0.010467529296875 -0.159454345703125 0.934271240234375 +0.06912500000000001 -0.010772705078125 -0.164398193359375 0.934271240234375 +0.06925000000000001 -0.0111083984375 -0.169342041015625 0.934271240234375 +0.06937500000000001 -0.0111083984375 -0.169342041015625 0.934271240234375 +0.06950000000000001 -0.01141357421875 -0.17425537109375 0.934271240234375 +0.06962500000000001 -0.01141357421875 -0.17425537109375 0.934271240234375 +0.06975000000000001 -0.011749267578125 -0.17913818359375 0.934271240234375 +0.06987500000000001 -0.012054443359375 -0.18402099609375 0.934271240234375 +0.07000000000000001 -0.012054443359375 -0.18402099609375 0.934271240234375 +0.07012500000000001 -0.01239013671875 -0.188873291015625 0.934271240234375 +0.07025000000000001 -0.01239013671875 -0.188873291015625 0.934271240234375 +0.07037500000000001 -0.0126953125 -0.1937255859375 0.934271240234375 +0.07050000000000001 -0.013031005859375 -0.19854736328125 0.934271240234375 +0.07062500000000001 -0.013031005859375 -0.19854736328125 0.934271240234375 +0.07075000000000001 -0.013336181640625 -0.203338623046875 0.934271240234375 +0.07087500000000001 -0.013336181640625 -0.203338623046875 0.934271240234375 +0.07100000000000001 -0.013641357421875 -0.208099365234375 0.934271240234375 +0.07112500000000001 -0.013946533203125 -0.212860107421875 0.934271240234375 +0.07125000000000001 -0.013946533203125 -0.212860107421875 0.934271240234375 +0.07137500000000001 -0.0142822265625 -0.21759033203125 0.934271240234375 +0.07150000000000001 -0.0142822265625 -0.21759033203125 0.934271240234375 +0.07162500000000001 -0.01458740234375 -0.2222900390625 0.934271240234375 +0.07175000000000001 -0.014892578125 -0.226959228515625 0.934271240234375 +0.07187500000000001 -0.014892578125 -0.226959228515625 0.934271240234375 +0.07200000000000001 -0.01519775390625 -0.231597900390625 0.934271240234375 +0.072125 -0.01519775390625 -0.231597900390625 0.934271240234375 +0.07225 -0.0155029296875 -0.236236572265625 0.934271240234375 +0.072375 -0.01580810546875 -0.2408447265625 0.934271240234375 +0.0725 -0.01580810546875 -0.2408447265625 0.934271240234375 +0.072625 -0.016082763671875 -0.24542236328125 0.934271240234375 +0.07275 -0.016082763671875 -0.24542236328125 0.934271240234375 +0.072875 -0.016387939453125 -0.249969482421875 0.934271240234375 +0.073 -0.016693115234375 -0.25445556640625 0.934271240234375 +0.073125 -0.016693115234375 -0.25445556640625 0.934271240234375 +0.07325 -0.016998291015625 -0.25897216796875 0.934271240234375 +0.073375 -0.016998291015625 -0.25897216796875 0.934271240234375 +0.0735 -0.01727294921875 -0.263427734375 0.934271240234375 +0.073625 -0.017578125 -0.267852783203125 0.934271240234375 +0.07375 -0.017578125 -0.267852783203125 0.934271240234375 +0.073875 -0.017852783203125 -0.27227783203125 0.934271240234375 +0.074 -0.017852783203125 -0.27227783203125 0.934271240234375 +0.074125 -0.018157958984375 -0.276641845703125 0.934271240234375 +0.07425 -0.0184326171875 -0.280975341796875 0.934271240234375 +0.074375 -0.0184326171875 -0.280975341796875 0.934271240234375 +0.0745 -0.018707275390625 -0.285308837890625 0.934271240234375 +0.074625 -0.018707275390625 -0.285308837890625 0.934271240234375 +0.07475 -0.01898193359375 -0.289581298828125 0.934271240234375 +0.074875 -0.019287109375 -0.2938232421875 0.934271240234375 +0.075 -0.019287109375 -0.2938232421875 0.934271240234375 +0.075125 -0.019561767578125 -0.298065185546875 0.934271240234375 +0.07525 -0.019561767578125 -0.298065185546875 0.934271240234375 +0.075375 -0.01983642578125 -0.30224609375 0.934271240234375 +0.0755 -0.020111083984375 -0.306396484375 0.934271240234375 +0.075625 -0.020111083984375 -0.306396484375 0.934271240234375 +0.07575 -0.0203857421875 -0.310516357421875 0.934271240234375 +0.075875 -0.0203857421875 -0.310516357421875 0.934271240234375 +0.076 -0.0206298828125 -0.314605712890625 0.934271240234375 +0.076125 -0.020904541015625 -0.31866455078125 0.934271240234375 +0.07625 -0.020904541015625 -0.31866455078125 0.934271240234375 +0.076375 -0.02117919921875 -0.322662353515625 0.934271240234375 +0.0765 -0.02117919921875 -0.322662353515625 0.934271240234375 +0.076625 -0.02142333984375 -0.32666015625 0.934271240234375 +0.07675 -0.021697998046875 -0.330596923828125 0.934271240234375 +0.076875 -0.021697998046875 -0.330596923828125 0.934271240234375 +0.077 -0.021942138671875 -0.334503173828125 0.934271240234375 +0.077125 -0.021942138671875 -0.334503173828125 0.934271240234375 +0.07725 -0.022186279296875 -0.33837890625 0.934271240234375 +0.077375 -0.0224609375 -0.34222412109375 0.934271240234375 +0.0775 -0.0224609375 -0.34222412109375 0.934271240234375 +0.077625 -0.022705078125 -0.34600830078125 0.934271240234375 +0.07775 -0.022705078125 -0.34600830078125 0.934271240234375 +0.077875 -0.02294921875 -0.349761962890625 0.934271240234375 +0.078 -0.023193359375 -0.353485107421875 0.934271240234375 +0.078125 -0.023193359375 -0.353485107421875 0.934271240234375 +0.07825 -0.0234375 -0.357177734375 0.934271240234375 +0.078375 -0.0234375 -0.357177734375 0.934271240234375 +0.0785 -0.023681640625 -0.360809326171875 0.934271240234375 +0.078625 -0.023895263671875 -0.36444091796875 0.934271240234375 +0.07875 -0.023895263671875 -0.36444091796875 0.934271240234375 +0.078875 -0.024139404296875 -0.36798095703125 0.934271240234375 +0.079 -0.024139404296875 -0.36798095703125 0.934271240234375 +0.079125 -0.024383544921875 -0.37152099609375 0.934271240234375 +0.07925 -0.02459716796875 -0.375 0.934271240234375 +0.079375 -0.02459716796875 -0.375 0.934271240234375 +0.0795 -0.02484130859375 -0.378448486328125 0.934271240234375 +0.079625 -0.02484130859375 -0.378448486328125 0.934271240234375 +0.07975 -0.025054931640625 -0.3818359375 0.934271240234375 +0.079875 -0.0252685546875 -0.38519287109375 0.934271240234375 +0.08 -0.0252685546875 -0.38519287109375 0.934271240234375 +0.080125 -0.025482177734375 -0.388519287109375 0.934271240234375 +0.08025 -0.025482177734375 -0.388519287109375 0.934271240234375 +0.080375 -0.02569580078125 -0.39178466796875 0.934271240234375 +0.0805 -0.025909423828125 -0.39501953125 0.934271240234375 +0.080625 -0.025909423828125 -0.39501953125 0.934271240234375 +0.08075 -0.026123046875 -0.398193359375 0.934271240234375 +0.080875 -0.026123046875 -0.398193359375 0.934271240234375 +0.081 -0.026336669921875 -0.401336669921875 0.934271240234375 +0.081125 -0.02655029296875 -0.404449462890625 0.934271240234375 +0.08125 -0.02655029296875 -0.404449462890625 0.934271240234375 +0.081375 -0.0267333984375 -0.407501220703125 0.934271240234375 +0.0815 -0.0267333984375 -0.407501220703125 0.934271240234375 +0.081625 -0.026947021484375 -0.4105224609375 0.934271240234375 +0.08175 -0.027130126953125 -0.413482666015625 0.934271240234375 +0.081875 -0.027130126953125 -0.413482666015625 0.934271240234375 +0.082 -0.027313232421875 -0.416412353515625 0.934271240234375 +0.082125 -0.027313232421875 -0.416412353515625 0.934271240234375 +0.08225 -0.027496337890625 -0.419281005859375 0.934271240234375 +0.082375 -0.0277099609375 -0.422119140625 0.934271240234375 +0.0825 -0.0277099609375 -0.422119140625 0.934271240234375 +0.082625 -0.02789306640625 -0.424896240234375 0.934271240234375 +0.08275 -0.02789306640625 -0.424896240234375 0.934271240234375 +0.082875 -0.028045654296875 -0.4276123046875 0.934271240234375 +0.083 -0.028228759765625 -0.4302978515625 0.934271240234375 +0.083125 -0.028228759765625 -0.4302978515625 0.934271240234375 +0.08325 -0.028411865234375 -0.432952880859375 0.934271240234375 +0.083375 -0.028411865234375 -0.432952880859375 0.934271240234375 +0.0835 -0.028564453125 -0.435546875 0.934271240234375 +0.083625 -0.02874755859375 -0.438079833984375 0.934271240234375 +0.08375 -0.02874755859375 -0.438079833984375 0.934271240234375 +0.08387500000000001 -0.028900146484375 -0.440582275390625 0.934271240234375 +0.08400000000000001 -0.028900146484375 -0.440582275390625 0.934271240234375 +0.08412500000000001 -0.029083251953125 -0.44305419921875 0.934271240234375 +0.08425000000000001 -0.02923583984375 -0.4454345703125 0.934271240234375 +0.08437500000000001 -0.02923583984375 -0.4454345703125 0.934271240234375 +0.08450000000000001 -0.029388427734375 -0.447784423828125 0.934271240234375 +0.08462500000000001 -0.029388427734375 -0.447784423828125 0.934271240234375 +0.08475000000000001 -0.029541015625 -0.450103759765625 0.934271240234375 +0.08487500000000001 -0.029693603515625 -0.452362060546875 0.934271240234375 +0.08500000000000001 -0.029693603515625 -0.452362060546875 0.934271240234375 +0.08512500000000001 -0.029815673828125 -0.454559326171875 0.934271240234375 +0.08525000000000001 -0.029815673828125 -0.454559326171875 0.934271240234375 +0.08537500000000001 -0.02996826171875 -0.45672607421875 0.934271240234375 +0.08550000000000001 -0.030120849609375 -0.458831787109375 0.934271240234375 +0.08562500000000001 -0.030120849609375 -0.458831787109375 0.934271240234375 +0.08575000000000001 -0.030242919921875 -0.46087646484375 0.934271240234375 +0.08587500000000001 -0.030242919921875 -0.46087646484375 0.934271240234375 +0.08600000000000001 -0.030364990234375 -0.462890625 0.934271240234375 +0.08612500000000001 -0.030487060546875 -0.464813232421875 0.934271240234375 +0.08625000000000001 -0.030487060546875 -0.464813232421875 0.934271240234375 +0.08637500000000001 -0.0306396484375 -0.46673583984375 0.934271240234375 +0.08650000000000001 -0.0306396484375 -0.46673583984375 0.934271240234375 +0.08662500000000001 -0.030731201171875 -0.46856689453125 0.934271240234375 +0.08675000000000001 -0.030853271484375 -0.470367431640625 0.934271240234375 +0.08687500000000001 -0.030853271484375 -0.470367431640625 0.934271240234375 +0.08700000000000001 -0.030975341796875 -0.472137451171875 0.934271240234375 +0.08712500000000001 -0.030975341796875 -0.472137451171875 0.934271240234375 +0.08725000000000001 -0.031097412109375 -0.47381591796875 0.934271240234375 +0.08737500000000001 -0.03118896484375 -0.4754638671875 0.934271240234375 +0.08750000000000001 -0.03118896484375 -0.4754638671875 0.934271240234375 +0.08762500000000001 -0.03131103515625 -0.47705078125 0.934271240234375 +0.08775000000000001 -0.03131103515625 -0.47705078125 0.934271240234375 +0.08787500000000001 -0.031402587890625 -0.478607177734375 0.934271240234375 +0.088 -0.031494140625 -0.480072021484375 0.934271240234375 +0.088125 -0.031494140625 -0.480072021484375 0.934271240234375 +0.08825 -0.031585693359375 -0.48150634765625 0.934271240234375 +0.088375 -0.031585693359375 -0.48150634765625 0.934271240234375 +0.0885 -0.03167724609375 -0.48291015625 0.934271240234375 +0.088625 -0.031768798828125 -0.484222412109375 0.934271240234375 +0.08875 -0.031768798828125 -0.484222412109375 0.934271240234375 +0.088875 -0.0318603515625 -0.485504150390625 0.934271240234375 +0.089 -0.0318603515625 -0.485504150390625 0.934271240234375 +0.089125 -0.031951904296875 -0.486724853515625 0.934271240234375 +0.08925 -0.032012939453125 -0.487884521484375 0.934271240234375 +0.089375 -0.032012939453125 -0.487884521484375 0.934271240234375 +0.0895 -0.032073974609375 -0.489013671875 0.934271240234375 +0.089625 -0.032073974609375 -0.489013671875 0.934271240234375 +0.08975 -0.03216552734375 -0.490081787109375 0.934271240234375 +0.089875 -0.0322265625 -0.4910888671875 0.934271240234375 +0.09 -0.0322265625 -0.4910888671875 0.934271240234375 +0.090125 -0.03228759765625 -0.492034912109375 0.934271240234375 +0.09025 -0.03228759765625 -0.492034912109375 0.934271240234375 +0.090375 -0.0323486328125 -0.492950439453125 0.934271240234375 +0.0905 -0.03240966796875 -0.4937744140625 0.934271240234375 +0.090625 -0.03240966796875 -0.4937744140625 0.934271240234375 +0.09075 -0.032440185546875 -0.49456787109375 0.934271240234375 +0.090875 -0.032440185546875 -0.49456787109375 0.934271240234375 +0.091 -0.032501220703125 -0.49530029296875 0.934271240234375 +0.091125 -0.03253173828125 -0.496002197265625 0.934271240234375 +0.09125 -0.03253173828125 -0.496002197265625 0.934271240234375 +0.091375 -0.0325927734375 -0.496612548828125 0.934271240234375 +0.0915 -0.0325927734375 -0.496612548828125 0.934271240234375 +0.091625 -0.032623291015625 -0.4971923828125 0.934271240234375 +0.09175 -0.03265380859375 -0.497711181640625 0.934271240234375 +0.091875 -0.03265380859375 -0.497711181640625 0.934271240234375 +0.092 -0.032684326171875 -0.498199462890625 0.934271240234375 +0.092125 -0.032684326171875 -0.498199462890625 0.934271240234375 +0.09225 -0.03271484375 -0.49859619140625 0.934271240234375 +0.092375 -0.032745361328125 -0.49896240234375 0.934271240234375 +0.0925 -0.032745361328125 -0.49896240234375 0.934271240234375 +0.092625 -0.032745361328125 -0.499267578125 0.934271240234375 +0.09275 -0.032745361328125 -0.499267578125 0.934271240234375 +0.092875 -0.03277587890625 -0.49951171875 0.934271240234375 +0.093 -0.03277587890625 -0.49969482421875 0.934271240234375 +0.093125 -0.03277587890625 -0.49969482421875 0.934271240234375 +0.09325 -0.032806396484375 -0.49981689453125 0.934271240234375 +0.093375 -0.032806396484375 -0.49981689453125 0.934271240234375 +0.0935 -0.032806396484375 -0.499908447265625 0.934271240234375 +0.093625 -0.032806396484375 -0.49993896484375 0.934271240234375 +0.09375 -0.032806396484375 -0.49993896484375 0.934271240234375 +0.093875 -0.032806396484375 -0.499908447265625 0.934271240234375 +0.094 -0.032806396484375 -0.499908447265625 0.934271240234375 +0.094125 -0.032806396484375 -0.49981689453125 0.934271240234375 +0.09425 -0.03277587890625 -0.49969482421875 0.934271240234375 +0.094375 -0.03277587890625 -0.49969482421875 0.934271240234375 +0.0945 -0.03277587890625 -0.49951171875 0.934271240234375 +0.094625 -0.03277587890625 -0.49951171875 0.934271240234375 +0.09475 -0.032745361328125 -0.499267578125 0.934271240234375 +0.094875 -0.032745361328125 -0.49896240234375 0.934271240234375 +0.095 -0.032745361328125 -0.49896240234375 0.934271240234375 +0.095125 -0.03271484375 -0.49859619140625 0.934271240234375 +0.09525 -0.03271484375 -0.49859619140625 0.934271240234375 +0.095375 -0.032684326171875 -0.498199462890625 0.934271240234375 +0.0955 -0.03265380859375 -0.497711181640625 0.934271240234375 +0.095625 -0.03265380859375 -0.497711181640625 0.934271240234375 +0.09575 -0.032623291015625 -0.4971923828125 0.934271240234375 +0.095875 -0.032623291015625 -0.4971923828125 0.934271240234375 +0.096 -0.00030517578125 -0.496612548828125 0.99923095703125 +0.096125 -0.00030517578125 -0.496002197265625 0.99923095703125 +0.09625 -0.00030517578125 -0.496002197265625 0.99923095703125 +0.096375 -0.00030517578125 -0.49530029296875 0.99923095703125 +0.0965 -0.00030517578125 -0.49530029296875 0.99923095703125 +0.096625 -0.00030517578125 -0.49456787109375 0.99923095703125 +0.09675 -0.00030517578125 -0.4937744140625 0.99923095703125 +0.096875 -0.00030517578125 -0.4937744140625 0.99923095703125 +0.097 -0.00030517578125 -0.492950439453125 0.99923095703125 +0.097125 -0.00030517578125 -0.492950439453125 0.99923095703125 +0.09725 -0.00030517578125 -0.492034912109375 0.99923095703125 +0.097375 -0.00030517578125 -0.4910888671875 0.99923095703125 +0.0975 -0.00030517578125 -0.4910888671875 0.99923095703125 +0.097625 -0.00030517578125 -0.490081787109375 0.99923095703125 +0.09775 -0.00030517578125 -0.490081787109375 0.99923095703125 +0.097875 -0.00030517578125 -0.489013671875 0.99923095703125 +0.098 -0.00030517578125 -0.487884521484375 0.99923095703125 +0.098125 -0.00030517578125 -0.487884521484375 0.99923095703125 +0.09825 -0.00030517578125 -0.486724853515625 0.99923095703125 +0.098375 -0.00030517578125 -0.486724853515625 0.99923095703125 +0.0985 -0.00030517578125 -0.485504150390625 0.99923095703125 +0.098625 -0.00030517578125 -0.484222412109375 0.99923095703125 +0.09875 -0.00030517578125 -0.484222412109375 0.99923095703125 +0.098875 -0.00030517578125 -0.48291015625 0.99923095703125 +0.099 -0.00030517578125 -0.48291015625 0.99923095703125 +0.099125 -0.00030517578125 -0.48150634765625 0.99923095703125 +0.09925 -0.00030517578125 -0.480072021484375 0.99923095703125 +0.099375 -0.00030517578125 -0.480072021484375 0.99923095703125 +0.0995 -0.00030517578125 -0.478607177734375 0.99923095703125 +0.099625 -0.00030517578125 -0.478607177734375 0.99923095703125 +0.09975 -0.00030517578125 -0.47705078125 0.99923095703125 +0.099875 -0.00030517578125 -0.4754638671875 0.99923095703125 +0.1 -0.00030517578125 -0.4754638671875 0.99923095703125 +0.100125 -0.00030517578125 -0.47381591796875 0.99923095703125 +0.10025 -0.00030517578125 -0.47381591796875 0.99923095703125 +0.100375 -0.00030517578125 -0.472137451171875 0.99923095703125 +0.1005 -0.00030517578125 -0.470367431640625 0.99923095703125 +0.100625 -0.00030517578125 -0.470367431640625 0.99923095703125 +0.10075 -0.00030517578125 -0.46856689453125 0.99923095703125 +0.100875 -0.00030517578125 -0.46856689453125 0.99923095703125 +0.101 -0.00030517578125 -0.46673583984375 0.99923095703125 +0.101125 -0.00030517578125 -0.464813232421875 0.99923095703125 +0.10125 -0.00030517578125 -0.464813232421875 0.99923095703125 +0.101375 -0.00030517578125 -0.462890625 0.99923095703125 +0.1015 -0.00030517578125 -0.462890625 0.99923095703125 +0.1016250000000000128 -0.00030517578125 -0.46087646484375 0.99923095703125 +0.1017500000000000128 -0.00030517578125 -0.458831787109375 0.99923095703125 +0.1018750000000000128 -0.00030517578125 -0.458831787109375 0.99923095703125 +0.1020000000000000128 -0.00030517578125 -0.45672607421875 0.99923095703125 +0.1021250000000000128 -0.00030517578125 -0.45672607421875 0.99923095703125 +0.1022500000000000128 -0.00030517578125 -0.454559326171875 0.99923095703125 +0.1023750000000000128 -0.000274658203125 -0.452362060546875 0.99923095703125 +0.1025000000000000128 -0.000274658203125 -0.452362060546875 0.99923095703125 +0.1026250000000000128 -0.000274658203125 -0.450103759765625 0.99923095703125 +0.1027500000000000128 -0.000274658203125 -0.450103759765625 0.99923095703125 +0.1028750000000000128 -0.000274658203125 -0.447784423828125 0.99923095703125 +0.1030000000000000128 -0.000274658203125 -0.4454345703125 0.99923095703125 +0.1031250000000000128 -0.000274658203125 -0.4454345703125 0.99923095703125 +0.1032500000000000128 -0.000274658203125 -0.44305419921875 0.99923095703125 +0.1033750000000000128 -0.000274658203125 -0.44305419921875 0.99923095703125 +0.1035000000000000128 -0.000274658203125 -0.440582275390625 0.99923095703125 +0.1036250000000000128 -0.000274658203125 -0.438079833984375 0.99923095703125 +0.1037500000000000128 -0.000274658203125 -0.438079833984375 0.99923095703125 +0.1038750000000000128 -0.000274658203125 -0.435546875 0.99923095703125 +0.104000000000000016 -0.000274658203125 -0.435546875 0.99923095703125 +0.104125 -0.000274658203125 -0.432952880859375 0.99923095703125 +0.10425 -0.000274658203125 -0.4302978515625 0.99923095703125 +0.104375 -0.000274658203125 -0.4302978515625 0.99923095703125 +0.1045 -0.000274658203125 -0.4276123046875 0.99923095703125 +0.104625 -0.000274658203125 -0.4276123046875 0.99923095703125 +0.10475 -0.000274658203125 -0.424896240234375 0.99923095703125 +0.104875 -0.000274658203125 -0.422119140625 0.99923095703125 +0.105 -0.000274658203125 -0.422119140625 0.99923095703125 +0.105125 -0.000274658203125 -0.419281005859375 0.99923095703125 +0.10525 -0.000274658203125 -0.419281005859375 0.99923095703125 +0.105375 -0.000274658203125 -0.416412353515625 0.99923095703125 +0.1055 -0.000274658203125 -0.413482666015625 0.99923095703125 +0.105625 -0.000274658203125 -0.413482666015625 0.99923095703125 +0.10575 -0.000274658203125 -0.4105224609375 0.99923095703125 +0.105875 -0.000274658203125 -0.4105224609375 0.99923095703125 +0.106 -0.000244140625 -0.407501220703125 0.99923095703125 +0.106125 -0.000244140625 -0.404449462890625 0.99923095703125 +0.10625 -0.000244140625 -0.404449462890625 0.99923095703125 +0.106375 -0.000244140625 -0.401336669921875 0.99923095703125 +0.1065 -0.000244140625 -0.401336669921875 0.99923095703125 +0.106625 -0.000244140625 -0.398193359375 0.99923095703125 +0.10675 -0.000244140625 -0.39501953125 0.99923095703125 +0.106875 -0.000244140625 -0.39501953125 0.99923095703125 +0.107 -0.000244140625 -0.39178466796875 0.99923095703125 +0.107125 -0.000244140625 -0.39178466796875 0.99923095703125 +0.10725 -0.000244140625 -0.388519287109375 0.99923095703125 +0.107375 -0.000244140625 -0.38519287109375 0.99923095703125 +0.1075 -0.000244140625 -0.38519287109375 0.99923095703125 +0.107625 -0.000244140625 -0.3818359375 0.99923095703125 +0.10775 -0.000244140625 -0.3818359375 0.99923095703125 +0.107875 -0.000244140625 -0.378448486328125 0.99923095703125 +0.108 -0.000244140625 -0.375 0.99923095703125 +0.108125 -0.000244140625 -0.375 0.99923095703125 +0.10825 -0.000244140625 -0.37152099609375 0.99923095703125 +0.108375 -0.000244140625 -0.37152099609375 0.99923095703125 +0.1085 -0.000244140625 -0.36798095703125 0.99923095703125 +0.108625 -0.000244140625 -0.36444091796875 0.99923095703125 +0.10875 -0.000244140625 -0.36444091796875 0.99923095703125 +0.108875 -0.000213623046875 -0.360809326171875 0.99923095703125 +0.109 -0.000213623046875 -0.360809326171875 0.99923095703125 +0.109125 -0.000213623046875 -0.357177734375 0.99923095703125 +0.10925 -0.000213623046875 -0.353485107421875 0.99923095703125 +0.109375 -0.000213623046875 -0.353485107421875 0.99923095703125 +0.1095 -0.000213623046875 -0.349761962890625 0.99923095703125 +0.109625 -0.000213623046875 -0.349761962890625 0.99923095703125 +0.10975 -0.000213623046875 -0.34600830078125 0.99923095703125 +0.109875 -0.000213623046875 -0.34222412109375 0.99923095703125 +0.11 -0.000213623046875 -0.34222412109375 0.99923095703125 +0.110125 -0.000213623046875 -0.33837890625 0.99923095703125 +0.11025 -0.000213623046875 -0.33837890625 0.99923095703125 +0.110375 -0.000213623046875 -0.334503173828125 0.99923095703125 +0.1105 -0.000213623046875 -0.330596923828125 0.99923095703125 +0.110625 -0.000213623046875 -0.330596923828125 0.99923095703125 +0.11075 -0.000213623046875 -0.32666015625 0.99923095703125 +0.110875 -0.000213623046875 -0.32666015625 0.99923095703125 +0.111 -0.000213623046875 -0.322662353515625 0.99923095703125 +0.111125 -0.000213623046875 -0.31866455078125 0.99923095703125 +0.11125 -0.000213623046875 -0.31866455078125 0.99923095703125 +0.111375 -0.00018310546875 -0.314605712890625 0.99923095703125 +0.1115 -0.00018310546875 -0.314605712890625 0.99923095703125 +0.111625 -0.00018310546875 -0.310516357421875 0.99923095703125 +0.11175 -0.00018310546875 -0.306396484375 0.99923095703125 +0.111875 -0.00018310546875 -0.306396484375 0.99923095703125 +0.112 -0.00018310546875 -0.30224609375 0.99923095703125 +0.112125 -0.00018310546875 -0.30224609375 0.99923095703125 +0.11225 -0.00018310546875 -0.298065185546875 0.99923095703125 +0.112375 -0.00018310546875 -0.2938232421875 0.99923095703125 +0.1125 -0.00018310546875 -0.2938232421875 0.99923095703125 +0.112625 -0.00018310546875 -0.289581298828125 0.99923095703125 +0.11275 -0.00018310546875 -0.289581298828125 0.99923095703125 +0.112875 -0.00018310546875 -0.285308837890625 0.99923095703125 +0.113 -0.00018310546875 -0.280975341796875 0.99923095703125 +0.113125 -0.00018310546875 -0.280975341796875 0.99923095703125 +0.11325 -0.00018310546875 -0.276641845703125 0.99923095703125 +0.113375 -0.00018310546875 -0.276641845703125 0.99923095703125 +0.1135 -0.000152587890625 -0.27227783203125 0.99923095703125 +0.113625 -0.000152587890625 -0.267852783203125 0.99923095703125 +0.11375 -0.000152587890625 -0.267852783203125 0.99923095703125 +0.113875 -0.000152587890625 -0.263427734375 0.99923095703125 +0.114 -0.000152587890625 -0.263427734375 0.99923095703125 +0.114125 -0.000152587890625 -0.25897216796875 0.99923095703125 +0.11425 -0.000152587890625 -0.25445556640625 0.99923095703125 +0.114375 -0.000152587890625 -0.25445556640625 0.99923095703125 +0.1145 -0.000152587890625 -0.249969482421875 0.99923095703125 +0.114625 -0.000152587890625 -0.249969482421875 0.99923095703125 +0.11475 -0.000152587890625 -0.24542236328125 0.99923095703125 +0.114875 -0.000152587890625 -0.2408447265625 0.99923095703125 +0.115 -0.000152587890625 -0.2408447265625 0.99923095703125 +0.115125 -0.000152587890625 -0.236236572265625 0.99923095703125 +0.11525 -0.000152587890625 -0.236236572265625 0.99923095703125 +0.115375 -0.000152587890625 -0.231597900390625 0.99923095703125 +0.1155 -0.0001220703125 -0.226959228515625 0.99923095703125 +0.115625 -0.0001220703125 -0.226959228515625 0.99923095703125 +0.11575 -0.0001220703125 -0.2222900390625 0.99923095703125 +0.115875 -0.0001220703125 -0.2222900390625 0.99923095703125 +0.116 -0.0001220703125 -0.21759033203125 0.99923095703125 +0.116125 -0.0001220703125 -0.212860107421875 0.99923095703125 +0.11625 -0.0001220703125 -0.212860107421875 0.99923095703125 +0.116375 -0.0001220703125 -0.208099365234375 0.99923095703125 +0.1165 -0.0001220703125 -0.208099365234375 0.99923095703125 +0.116625 -0.0001220703125 -0.203338623046875 0.99923095703125 +0.11675 -0.0001220703125 -0.19854736328125 0.99923095703125 +0.116875 -0.0001220703125 -0.19854736328125 0.99923095703125 +0.117 -0.0001220703125 -0.1937255859375 0.99923095703125 +0.117125 -0.0001220703125 -0.1937255859375 0.99923095703125 +0.117250000000000013 -0.0001220703125 -0.188873291015625 0.99923095703125 +0.117375000000000013 -0.0001220703125 -0.18402099609375 0.99923095703125 +0.117500000000000013 -0.0001220703125 -0.18402099609375 0.99923095703125 +0.117625000000000013 -9.1552734375e-05 -0.17913818359375 0.99923095703125 +0.117750000000000013 -9.1552734375e-05 -0.17913818359375 0.99923095703125 +0.117875000000000013 -9.1552734375e-05 -0.17425537109375 0.99923095703125 +0.118000000000000013 -9.1552734375e-05 -0.169342041015625 0.99923095703125 +0.118125000000000013 -9.1552734375e-05 -0.169342041015625 0.99923095703125 +0.118250000000000013 -9.1552734375e-05 -0.164398193359375 0.99923095703125 +0.118375000000000013 -9.1552734375e-05 -0.164398193359375 0.99923095703125 +0.118500000000000013 -9.1552734375e-05 -0.159454345703125 0.99923095703125 +0.118625000000000013 -9.1552734375e-05 -0.15447998046875 0.99923095703125 +0.118750000000000013 -9.1552734375e-05 -0.15447998046875 0.99923095703125 +0.118875000000000013 -9.1552734375e-05 -0.14947509765625 0.99923095703125 +0.119000000000000013 -9.1552734375e-05 -0.14947509765625 0.99923095703125 +0.119125000000000013 -9.1552734375e-05 -0.14447021484375 0.99923095703125 +0.119250000000000013 -9.1552734375e-05 -0.13946533203125 0.99923095703125 +0.119375000000000013 -9.1552734375e-05 -0.13946533203125 0.99923095703125 +0.119500000000000013 -6.103515625e-05 -0.134429931640625 0.99923095703125 +0.119625000000000016 -6.103515625e-05 -0.134429931640625 0.99923095703125 +0.119750000000000016 -6.103515625e-05 -0.129364013671875 0.99923095703125 +0.119875000000000016 -6.103515625e-05 -0.124298095703125 0.99923095703125 +0.12 -6.103515625e-05 -0.124298095703125 0.99923095703125 +0.120125 -6.103515625e-05 -0.119232177734375 0.99923095703125 +0.12025 -6.103515625e-05 -0.119232177734375 0.99923095703125 +0.120375 -6.103515625e-05 -0.1141357421875 0.99923095703125 +0.1205 -6.103515625e-05 -0.109039306640625 0.99923095703125 +0.120625 -6.103515625e-05 -0.109039306640625 0.99923095703125 +0.12075 -6.103515625e-05 -0.103912353515625 0.99923095703125 +0.120875 -6.103515625e-05 -0.103912353515625 0.99923095703125 +0.121 -6.103515625e-05 -0.098785400390625 0.99923095703125 +0.121125 -6.103515625e-05 -0.093658447265625 0.99923095703125 +0.12125 -6.103515625e-05 -0.093658447265625 0.99923095703125 +0.121375 -3.0517578125e-05 -0.0885009765625 0.99923095703125 +0.1215 -3.0517578125e-05 -0.0885009765625 0.99923095703125 +0.121625 -3.0517578125e-05 -0.083343505859375 0.99923095703125 +0.12175 -3.0517578125e-05 -0.07818603515625 0.99923095703125 +0.121875 -3.0517578125e-05 -0.07818603515625 0.99923095703125 +0.122 -3.0517578125e-05 -0.072998046875 0.99923095703125 +0.122125 -3.0517578125e-05 -0.072998046875 0.99923095703125 +0.12225 -3.0517578125e-05 -0.06781005859375 0.99923095703125 +0.122375 -3.0517578125e-05 -0.0626220703125 0.99923095703125 +0.1225 -3.0517578125e-05 -0.0626220703125 0.99923095703125 +0.122625 -3.0517578125e-05 -0.05743408203125 0.99923095703125 +0.12275 -3.0517578125e-05 -0.05743408203125 0.99923095703125 +0.122875 -3.0517578125e-05 -0.05224609375 0.99923095703125 +0.123 -3.0517578125e-05 -0.047027587890625 0.99923095703125 +0.123125 -3.0517578125e-05 -0.047027587890625 0.99923095703125 +0.12325 0.0 -0.04180908203125 0.99923095703125 +0.123375 0.0 -0.04180908203125 0.99923095703125 +0.1235 0.0 -0.036590576171875 0.99923095703125 +0.123625 0.0 -0.0313720703125 0.99923095703125 +0.12375 0.0 -0.0313720703125 0.99923095703125 +0.123875 0.0 -0.026123046875 0.99923095703125 +0.124 0.0 -0.026123046875 0.99923095703125 +0.124125 0.0 -0.020904541015625 0.99923095703125 +0.12425 0.0 -0.01568603515625 0.99923095703125 +0.124375 0.0 -0.01568603515625 0.99923095703125 +0.1245 0.0 -0.01043701171875 0.99923095703125 +0.124625 0.0 -0.01043701171875 0.99923095703125 +0.12475 0.0 -0.005218505859375 0.99923095703125 +0.124875 0.0 0.0 0.99923095703125 +0.125 0.0 0.0 0.99923095703125 +0.12512500000000002 0.0 0.005218505859375 0.99923095703125 +0.12525 0.0 0.005218505859375 0.99923095703125 +0.12537500000000002 0.0 0.01043701171875 0.99923095703125 +0.1255 0.0 0.01568603515625 0.99923095703125 +0.12562500000000002 0.0 0.01568603515625 0.99923095703125 +0.12575 0.0 0.020904541015625 0.99923095703125 +0.12587500000000002 0.0 0.020904541015625 0.99923095703125 +0.126 0.0 0.026123046875 0.99923095703125 +0.12612500000000002 0.0 0.0313720703125 0.99923095703125 +0.12625 0.0 0.0313720703125 0.99923095703125 +0.12637500000000002 0.0 0.036590576171875 0.99923095703125 +0.1265 0.0 0.036590576171875 0.99923095703125 +0.12662500000000002 0.0 0.04180908203125 0.99923095703125 +0.12675 3.0517578125e-05 0.047027587890625 0.99923095703125 +0.12687500000000002 3.0517578125e-05 0.047027587890625 0.99923095703125 +0.127 3.0517578125e-05 0.05224609375 0.99923095703125 +0.12712500000000002 3.0517578125e-05 0.05224609375 0.99923095703125 +0.12725 3.0517578125e-05 0.05743408203125 0.99923095703125 +0.12737500000000002 3.0517578125e-05 0.0626220703125 0.99923095703125 +0.1275 3.0517578125e-05 0.0626220703125 0.99923095703125 +0.12762500000000002 3.0517578125e-05 0.06781005859375 0.99923095703125 +0.12775 3.0517578125e-05 0.06781005859375 0.99923095703125 +0.12787500000000002 3.0517578125e-05 0.072998046875 0.99923095703125 +0.128 0.007415771484375 0.07818603515625 0.90478515625 +0.128125 0.007415771484375 0.07818603515625 0.90478515625 +0.12825 0.007904052734375 0.083343505859375 0.90478515625 +0.128375 0.007904052734375 0.083343505859375 0.90478515625 +0.1285 0.008392333984375 0.0885009765625 0.90478515625 +0.128625 0.008880615234375 0.093658447265625 0.90478515625 +0.12875 0.008880615234375 0.093658447265625 0.90478515625 +0.128875 0.0093994140625 0.098785400390625 0.90478515625 +0.129 0.0093994140625 0.098785400390625 0.90478515625 +0.129125 0.009857177734375 0.103912353515625 0.90478515625 +0.12925 0.010345458984375 0.109039306640625 0.90478515625 +0.129375 0.010345458984375 0.109039306640625 0.90478515625 +0.1295 0.010833740234375 0.1141357421875 0.90478515625 +0.129625 0.010833740234375 0.1141357421875 0.90478515625 +0.12975 0.011322021484375 0.119232177734375 0.90478515625 +0.129875 0.011810302734375 0.124298095703125 0.90478515625 +0.13 0.011810302734375 0.124298095703125 0.90478515625 +0.130125 0.012298583984375 0.129364013671875 0.90478515625 +0.13025 0.012298583984375 0.129364013671875 0.90478515625 +0.130375 0.012786865234375 0.134429931640625 0.90478515625 +0.1305 0.01324462890625 0.13946533203125 0.90478515625 +0.130625 0.01324462890625 0.13946533203125 0.90478515625 +0.13075 0.01373291015625 0.14447021484375 0.90478515625 +0.130875 0.01373291015625 0.14447021484375 0.90478515625 +0.131 0.01422119140625 0.14947509765625 0.90478515625 +0.131125 0.014678955078125 0.15447998046875 0.90478515625 +0.13125 0.014678955078125 0.15447998046875 0.90478515625 +0.131375 0.015167236328125 0.159454345703125 0.90478515625 +0.1315 0.015167236328125 0.159454345703125 0.90478515625 +0.131625 0.015625 0.164398193359375 0.90478515625 +0.13175 0.016082763671875 0.169342041015625 0.90478515625 +0.131875 0.016082763671875 0.169342041015625 0.90478515625 +0.132 0.016571044921875 0.17425537109375 0.90478515625 +0.132125 0.016571044921875 0.17425537109375 0.90478515625 +0.13225 0.01702880859375 0.17913818359375 0.90478515625 +0.132375 0.017486572265625 0.18402099609375 0.90478515625 +0.1325 0.017486572265625 0.18402099609375 0.90478515625 +0.132625 0.0179443359375 0.188873291015625 0.90478515625 +0.13275 0.0179443359375 0.188873291015625 0.90478515625 +0.132875 0.018402099609375 0.1937255859375 0.90478515625 +0.133 0.01885986328125 0.19854736328125 0.90478515625 +0.133125 0.01885986328125 0.19854736328125 0.90478515625 +0.13325 0.019317626953125 0.203338623046875 0.90478515625 +0.133375 0.019317626953125 0.203338623046875 0.90478515625 +0.1335 0.019775390625 0.208099365234375 0.90478515625 +0.133625 0.020233154296875 0.212860107421875 0.90478515625 +0.13375 0.020233154296875 0.212860107421875 0.90478515625 +0.133875 0.02069091796875 0.21759033203125 0.90478515625 +0.134 0.02069091796875 0.21759033203125 0.90478515625 +0.134125 0.0211181640625 0.2222900390625 0.90478515625 +0.13425 0.021575927734375 0.226959228515625 0.90478515625 +0.134375 0.021575927734375 0.226959228515625 0.90478515625 +0.1345 0.022003173828125 0.231597900390625 0.90478515625 +0.134625 0.022003173828125 0.231597900390625 0.90478515625 +0.13475 0.0224609375 0.236236572265625 0.90478515625 +0.134875 0.02288818359375 0.2408447265625 0.90478515625 +0.135 0.02288818359375 0.2408447265625 0.90478515625 +0.135125 0.023345947265625 0.24542236328125 0.90478515625 +0.13525 0.023345947265625 0.24542236328125 0.90478515625 +0.135375 0.023773193359375 0.249969482421875 0.90478515625 +0.1355 0.024200439453125 0.25445556640625 0.90478515625 +0.135625 0.024200439453125 0.25445556640625 0.90478515625 +0.13575 0.024627685546875 0.25897216796875 0.90478515625 +0.135875 0.024627685546875 0.25897216796875 0.90478515625 +0.136 0.025054931640625 0.263427734375 0.90478515625 +0.136125 0.025482177734375 0.267852783203125 0.90478515625 +0.13625 0.025482177734375 0.267852783203125 0.90478515625 +0.136375 0.02587890625 0.27227783203125 0.90478515625 +0.1365 0.02587890625 0.27227783203125 0.90478515625 +0.136625 0.02630615234375 0.276641845703125 0.90478515625 +0.13675 0.026702880859375 0.280975341796875 0.90478515625 +0.136875 0.026702880859375 0.280975341796875 0.90478515625 +0.137 0.027130126953125 0.285308837890625 0.90478515625 +0.137125 0.027130126953125 0.285308837890625 0.90478515625 +0.13725 0.02752685546875 0.289581298828125 0.90478515625 +0.137375 0.027923583984375 0.2938232421875 0.90478515625 +0.1375 0.027923583984375 0.2938232421875 0.90478515625 +0.137625 0.028350830078125 0.298065185546875 0.90478515625 +0.13775 0.028350830078125 0.298065185546875 0.90478515625 +0.137875 0.02874755859375 0.30224609375 0.90478515625 +0.138 0.029144287109375 0.306396484375 0.90478515625 +0.138125 0.029144287109375 0.306396484375 0.90478515625 +0.13825 0.029541015625 0.310516357421875 0.90478515625 +0.138375 0.029541015625 0.310516357421875 0.90478515625 +0.1385 0.0299072265625 0.314605712890625 0.90478515625 +0.138625 0.030303955078125 0.31866455078125 0.90478515625 +0.13875 0.030303955078125 0.31866455078125 0.90478515625 +0.138875 0.030670166015625 0.322662353515625 0.90478515625 +0.139 0.030670166015625 0.322662353515625 0.90478515625 +0.139125 0.03106689453125 0.32666015625 0.90478515625 +0.13925 0.03143310546875 0.330596923828125 0.90478515625 +0.139375 0.03143310546875 0.330596923828125 0.90478515625 +0.1395 0.03179931640625 0.334503173828125 0.90478515625 +0.139625 0.03179931640625 0.334503173828125 0.90478515625 +0.13975 0.03216552734375 0.33837890625 0.90478515625 +0.139875 0.03253173828125 0.34222412109375 0.90478515625 +0.14 0.03253173828125 0.34222412109375 0.90478515625 +0.140125 0.03289794921875 0.34600830078125 0.90478515625 +0.14025 0.03289794921875 0.34600830078125 0.90478515625 +0.140375 0.03326416015625 0.349761962890625 0.90478515625 +0.1405 0.033599853515625 0.353485107421875 0.90478515625 +0.140625 0.033599853515625 0.353485107421875 0.90478515625 +0.14075000000000002 0.033966064453125 0.357177734375 0.90478515625 +0.140875 0.033966064453125 0.357177734375 0.90478515625 +0.14100000000000002 0.0343017578125 0.360809326171875 0.90478515625 +0.141125 0.03466796875 0.36444091796875 0.90478515625 +0.14125000000000002 0.03466796875 0.36444091796875 0.90478515625 +0.141375 0.035003662109375 0.36798095703125 0.90478515625 +0.14150000000000002 0.035003662109375 0.36798095703125 0.90478515625 +0.141625 0.03533935546875 0.37152099609375 0.90478515625 +0.14175000000000002 0.03564453125 0.375 0.90478515625 +0.141875 0.03564453125 0.375 0.90478515625 +0.14200000000000002 0.035980224609375 0.378448486328125 0.90478515625 +0.142125 0.035980224609375 0.378448486328125 0.90478515625 +0.14225000000000002 0.03631591796875 0.3818359375 0.90478515625 +0.142375 0.03662109375 0.38519287109375 0.90478515625 +0.14250000000000002 0.03662109375 0.38519287109375 0.90478515625 +0.142625 0.036956787109375 0.388519287109375 0.90478515625 +0.14275000000000002 0.036956787109375 0.388519287109375 0.90478515625 +0.142875 0.037261962890625 0.39178466796875 0.90478515625 +0.14300000000000002 0.037567138671875 0.39501953125 0.90478515625 +0.143125 0.037567138671875 0.39501953125 0.90478515625 +0.14325000000000002 0.037872314453125 0.398193359375 0.90478515625 +0.143375 0.037872314453125 0.398193359375 0.90478515625 +0.14350000000000002 0.038177490234375 0.401336669921875 0.90478515625 +0.143625 0.0384521484375 0.404449462890625 0.90478515625 +0.14375000000000002 0.0384521484375 0.404449462890625 0.90478515625 +0.143875 0.03875732421875 0.407501220703125 0.90478515625 +0.14400000000000002 0.03875732421875 0.407501220703125 0.90478515625 +0.144125 0.039031982421875 0.4105224609375 0.90478515625 +0.14425 0.039306640625 0.413482666015625 0.90478515625 +0.144375 0.039306640625 0.413482666015625 0.90478515625 +0.1445 0.03961181640625 0.416412353515625 0.90478515625 +0.144625 0.03961181640625 0.416412353515625 0.90478515625 +0.14475 0.03985595703125 0.419281005859375 0.90478515625 +0.144875 0.040130615234375 0.422119140625 0.90478515625 +0.145 0.040130615234375 0.422119140625 0.90478515625 +0.145125 0.0404052734375 0.424896240234375 0.90478515625 +0.14525 0.0404052734375 0.424896240234375 0.90478515625 +0.145375 0.040679931640625 0.4276123046875 0.90478515625 +0.1455 0.040924072265625 0.4302978515625 0.90478515625 +0.145625 0.040924072265625 0.4302978515625 0.90478515625 +0.14575 0.041168212890625 0.432952880859375 0.90478515625 +0.145875 0.041168212890625 0.432952880859375 0.90478515625 +0.146 0.041412353515625 0.435546875 0.90478515625 +0.146125 0.041656494140625 0.438079833984375 0.90478515625 +0.14625 0.041656494140625 0.438079833984375 0.90478515625 +0.146375 0.041900634765625 0.440582275390625 0.90478515625 +0.1465 0.041900634765625 0.440582275390625 0.90478515625 +0.146625 0.042144775390625 0.44305419921875 0.90478515625 +0.14675 0.0423583984375 0.4454345703125 0.90478515625 +0.146875 0.0423583984375 0.4454345703125 0.90478515625 +0.147 0.042572021484375 0.447784423828125 0.90478515625 +0.147125 0.042572021484375 0.447784423828125 0.90478515625 +0.14725 0.042816162109375 0.450103759765625 0.90478515625 +0.147375 0.04302978515625 0.452362060546875 0.90478515625 +0.1475 0.04302978515625 0.452362060546875 0.90478515625 +0.147625 0.043212890625 0.454559326171875 0.90478515625 +0.14775 0.043212890625 0.454559326171875 0.90478515625 +0.147875 0.043426513671875 0.45672607421875 0.90478515625 +0.148 0.04364013671875 0.458831787109375 0.90478515625 +0.148125 0.04364013671875 0.458831787109375 0.90478515625 +0.14825 0.0438232421875 0.46087646484375 0.90478515625 +0.148375 0.0438232421875 0.46087646484375 0.90478515625 +0.1485 0.04400634765625 0.462890625 0.90478515625 +0.148625 0.044189453125 0.464813232421875 0.90478515625 +0.14875 0.044189453125 0.464813232421875 0.90478515625 +0.148875 0.04437255859375 0.46673583984375 0.90478515625 +0.149 0.04437255859375 0.46673583984375 0.90478515625 +0.149125 0.0445556640625 0.46856689453125 0.90478515625 +0.14925 0.04473876953125 0.470367431640625 0.90478515625 +0.149375 0.04473876953125 0.470367431640625 0.90478515625 +0.1495 0.044891357421875 0.472137451171875 0.90478515625 +0.149625 0.044891357421875 0.472137451171875 0.90478515625 +0.14975 0.045074462890625 0.47381591796875 0.90478515625 +0.149875 0.04522705078125 0.4754638671875 0.90478515625 +0.15 0.04522705078125 0.4754638671875 0.90478515625 +0.150125 0.045379638671875 0.47705078125 0.90478515625 +0.15025 0.045379638671875 0.47705078125 0.90478515625 +0.150375 0.045501708984375 0.478607177734375 0.90478515625 +0.1505 0.045654296875 0.480072021484375 0.90478515625 +0.150625 0.045654296875 0.480072021484375 0.90478515625 +0.15075 0.0457763671875 0.48150634765625 0.90478515625 +0.150875 0.0457763671875 0.48150634765625 0.90478515625 +0.151 0.045928955078125 0.48291015625 0.90478515625 +0.151125 0.046051025390625 0.484222412109375 0.90478515625 +0.15125 0.046051025390625 0.484222412109375 0.90478515625 +0.151375 0.046173095703125 0.485504150390625 0.90478515625 +0.1515 0.046173095703125 0.485504150390625 0.90478515625 +0.151625 0.046295166015625 0.486724853515625 0.90478515625 +0.15175 0.04638671875 0.487884521484375 0.90478515625 +0.151875 0.04638671875 0.487884521484375 0.90478515625 +0.152 0.0465087890625 0.489013671875 0.90478515625 +0.152125 0.0465087890625 0.489013671875 0.90478515625 +0.15225 0.046600341796875 0.490081787109375 0.90478515625 +0.152375 0.04669189453125 0.4910888671875 0.90478515625 +0.1525 0.04669189453125 0.4910888671875 0.90478515625 +0.152625 0.046783447265625 0.492034912109375 0.90478515625 +0.15275 0.046783447265625 0.492034912109375 0.90478515625 +0.152875 0.046875 0.492950439453125 0.90478515625 +0.153 0.046966552734375 0.4937744140625 0.90478515625 +0.153125 0.046966552734375 0.4937744140625 0.90478515625 +0.15325 0.047027587890625 0.49456787109375 0.90478515625 +0.153375 0.047027587890625 0.49456787109375 0.90478515625 +0.1535 0.047088623046875 0.49530029296875 0.90478515625 +0.153625 0.04718017578125 0.496002197265625 0.90478515625 +0.15375 0.04718017578125 0.496002197265625 0.90478515625 +0.153875 0.0472412109375 0.496612548828125 0.90478515625 +0.154 0.0472412109375 0.496612548828125 0.90478515625 +0.154125 0.047271728515625 0.4971923828125 0.90478515625 +0.15425 0.047332763671875 0.497711181640625 0.90478515625 +0.154375 0.047332763671875 0.497711181640625 0.90478515625 +0.1545 0.04736328125 0.498199462890625 0.90478515625 +0.154625 0.04736328125 0.498199462890625 0.90478515625 +0.15475 0.04742431640625 0.49859619140625 0.90478515625 +0.154875 0.047454833984375 0.49896240234375 0.90478515625 +0.155 0.047454833984375 0.49896240234375 0.90478515625 +0.155125 0.0474853515625 0.499267578125 0.90478515625 +0.15525 0.0474853515625 0.499267578125 0.90478515625 +0.155375 0.047515869140625 0.49951171875 0.90478515625 +0.1555 0.047515869140625 0.49969482421875 0.90478515625 +0.155625 0.047515869140625 0.49969482421875 0.90478515625 +0.15575 0.04754638671875 0.49981689453125 0.90478515625 +0.155875 0.04754638671875 0.49981689453125 0.90478515625 +0.156 0.04754638671875 0.499908447265625 0.90478515625 +0.156125 0.04754638671875 0.49993896484375 0.90478515625 +0.15625 0.04754638671875 0.49993896484375 0.90478515625 +0.15637500000000002 0.04754638671875 0.499908447265625 0.90478515625 +0.1565 0.04754638671875 0.499908447265625 0.90478515625 +0.15662500000000002 0.04754638671875 0.49981689453125 0.90478515625 +0.15675 0.047515869140625 0.49969482421875 0.90478515625 +0.15687500000000002 0.047515869140625 0.49969482421875 0.90478515625 +0.157 0.047515869140625 0.49951171875 0.90478515625 +0.15712500000000002 0.047515869140625 0.49951171875 0.90478515625 +0.15725 0.0474853515625 0.499267578125 0.90478515625 +0.15737500000000002 0.047454833984375 0.49896240234375 0.90478515625 +0.1575 0.047454833984375 0.49896240234375 0.90478515625 +0.15762500000000002 0.04742431640625 0.49859619140625 0.90478515625 +0.15775 0.04742431640625 0.49859619140625 0.90478515625 +0.15787500000000002 0.04736328125 0.498199462890625 0.90478515625 +0.158 0.047332763671875 0.497711181640625 0.90478515625 +0.15812500000000002 0.047332763671875 0.497711181640625 0.90478515625 +0.15825 0.047271728515625 0.4971923828125 0.90478515625 +0.15837500000000002 0.047271728515625 0.4971923828125 0.90478515625 +0.1585 0.0472412109375 0.496612548828125 0.90478515625 +0.15862500000000002 0.04718017578125 0.496002197265625 0.90478515625 +0.15875 0.04718017578125 0.496002197265625 0.90478515625 +0.15887500000000002 0.047088623046875 0.49530029296875 0.90478515625 +0.159 0.047088623046875 0.49530029296875 0.90478515625 +0.15912500000000002 0.047027587890625 0.49456787109375 0.90478515625 +0.15925 0.046966552734375 0.4937744140625 0.90478515625 +0.15937500000000002 0.046966552734375 0.4937744140625 0.90478515625 +0.1595 0.046875 0.492950439453125 0.90478515625 +0.15962500000000002 0.046875 0.492950439453125 0.90478515625 +0.15975 0.046783447265625 0.492034912109375 0.90478515625 +0.15987500000000002 0.04669189453125 0.4910888671875 0.90478515625 +0.16 0.163970947265625 0.4910888671875 0.665972900390625 +0.160125 0.16363525390625 0.490081787109375 0.665972900390625 +0.16025 0.16363525390625 0.490081787109375 0.665972900390625 +0.160375 0.163299560546875 0.489013671875 0.665972900390625 +0.1605 0.16290283203125 0.487884521484375 0.665972900390625 +0.160625 0.16290283203125 0.487884521484375 0.665972900390625 +0.16075 0.16253662109375 0.486724853515625 0.665972900390625 +0.160875 0.16253662109375 0.486724853515625 0.665972900390625 +0.161 0.162109375 0.485504150390625 0.665972900390625 +0.161125 0.16168212890625 0.484222412109375 0.665972900390625 +0.16125 0.16168212890625 0.484222412109375 0.665972900390625 +0.161375 0.1612548828125 0.48291015625 0.665972900390625 +0.1615 0.1612548828125 0.48291015625 0.665972900390625 +0.161625 0.160797119140625 0.48150634765625 0.665972900390625 +0.16175 0.160308837890625 0.480072021484375 0.665972900390625 +0.161875 0.160308837890625 0.480072021484375 0.665972900390625 +0.162 0.159820556640625 0.478607177734375 0.665972900390625 +0.162125 0.159820556640625 0.478607177734375 0.665972900390625 +0.16225 0.1593017578125 0.47705078125 0.665972900390625 +0.162375 0.158782958984375 0.4754638671875 0.665972900390625 +0.1625 0.158782958984375 0.4754638671875 0.665972900390625 +0.162625 0.158203125 0.47381591796875 0.665972900390625 +0.16275 0.158203125 0.47381591796875 0.665972900390625 +0.162875 0.15765380859375 0.472137451171875 0.665972900390625 +0.163 0.157073974609375 0.470367431640625 0.665972900390625 +0.163125 0.157073974609375 0.470367431640625 0.665972900390625 +0.16325 0.156463623046875 0.46856689453125 0.665972900390625 +0.163375 0.156463623046875 0.46856689453125 0.665972900390625 +0.1635 0.155853271484375 0.46673583984375 0.665972900390625 +0.163625 0.15521240234375 0.464813232421875 0.665972900390625 +0.16375 0.15521240234375 0.464813232421875 0.665972900390625 +0.163875 0.154571533203125 0.462890625 0.665972900390625 +0.164 0.154571533203125 0.462890625 0.665972900390625 +0.164125 0.153900146484375 0.46087646484375 0.665972900390625 +0.16425 0.1531982421875 0.458831787109375 0.665972900390625 +0.164375 0.1531982421875 0.458831787109375 0.665972900390625 +0.1645 0.152496337890625 0.45672607421875 0.665972900390625 +0.164625 0.152496337890625 0.45672607421875 0.665972900390625 +0.16475 0.15179443359375 0.454559326171875 0.665972900390625 +0.164875 0.15106201171875 0.452362060546875 0.665972900390625 +0.165 0.15106201171875 0.452362060546875 0.665972900390625 +0.165125 0.150299072265625 0.450103759765625 0.665972900390625 +0.16525 0.150299072265625 0.450103759765625 0.665972900390625 +0.165375 0.1495361328125 0.447784423828125 0.665972900390625 +0.1655 0.14874267578125 0.4454345703125 0.665972900390625 +0.165625 0.14874267578125 0.4454345703125 0.665972900390625 +0.16575 0.14794921875 0.44305419921875 0.665972900390625 +0.165875 0.14794921875 0.44305419921875 0.665972900390625 +0.166 0.147125244140625 0.440582275390625 0.665972900390625 +0.166125 0.146270751953125 0.438079833984375 0.665972900390625 +0.16625 0.146270751953125 0.438079833984375 0.665972900390625 +0.166375 0.14544677734375 0.435546875 0.665972900390625 +0.1665 0.14544677734375 0.435546875 0.665972900390625 +0.166625 0.144561767578125 0.432952880859375 0.665972900390625 +0.16675 0.1436767578125 0.4302978515625 0.665972900390625 +0.166875 0.1436767578125 0.4302978515625 0.665972900390625 +0.167 0.142791748046875 0.4276123046875 0.665972900390625 +0.167125 0.142791748046875 0.4276123046875 0.665972900390625 +0.16725 0.141876220703125 0.424896240234375 0.665972900390625 +0.167375 0.140960693359375 0.422119140625 0.665972900390625 +0.1675 0.140960693359375 0.422119140625 0.665972900390625 +0.167625 0.1400146484375 0.419281005859375 0.665972900390625 +0.16775 0.1400146484375 0.419281005859375 0.665972900390625 +0.167875 0.1390380859375 0.416412353515625 0.665972900390625 +0.168 0.1380615234375 0.413482666015625 0.665972900390625 +0.168125 0.1380615234375 0.413482666015625 0.665972900390625 +0.16825 0.1370849609375 0.4105224609375 0.665972900390625 +0.168375 0.1370849609375 0.4105224609375 0.665972900390625 +0.1685 0.136077880859375 0.407501220703125 0.665972900390625 +0.168625 0.135040283203125 0.404449462890625 0.665972900390625 +0.16875 0.135040283203125 0.404449462890625 0.665972900390625 +0.168875 0.134002685546875 0.401336669921875 0.665972900390625 +0.169 0.134002685546875 0.401336669921875 0.665972900390625 +0.169125 0.132965087890625 0.398193359375 0.665972900390625 +0.16925 0.13189697265625 0.39501953125 0.665972900390625 +0.169375 0.13189697265625 0.39501953125 0.665972900390625 +0.1695 0.130828857421875 0.39178466796875 0.665972900390625 +0.169625 0.130828857421875 0.39178466796875 0.665972900390625 +0.16975 0.129730224609375 0.388519287109375 0.665972900390625 +0.169875 0.128631591796875 0.38519287109375 0.665972900390625 +0.17 0.128631591796875 0.38519287109375 0.665972900390625 +0.170125 0.12750244140625 0.3818359375 0.665972900390625 +0.17025 0.12750244140625 0.3818359375 0.665972900390625 +0.170375 0.126373291015625 0.378448486328125 0.665972900390625 +0.1705 0.125213623046875 0.375 0.665972900390625 +0.170625 0.125213623046875 0.375 0.665972900390625 +0.17075 0.124053955078125 0.37152099609375 0.665972900390625 +0.170875 0.124053955078125 0.37152099609375 0.665972900390625 +0.171 0.12286376953125 0.36798095703125 0.665972900390625 +0.171125 0.1217041015625 0.36444091796875 0.665972900390625 +0.17125 0.1217041015625 0.36444091796875 0.665972900390625 +0.171375 0.1204833984375 0.360809326171875 0.665972900390625 +0.1715 0.1204833984375 0.360809326171875 0.665972900390625 +0.171625 0.1192626953125 0.357177734375 0.665972900390625 +0.17175 0.1180419921875 0.353485107421875 0.665972900390625 +0.171875 0.1180419921875 0.353485107421875 0.665972900390625 +0.17200000000000002 0.116790771484375 0.349761962890625 0.665972900390625 +0.172125 0.116790771484375 0.349761962890625 0.665972900390625 +0.17225000000000002 0.11553955078125 0.34600830078125 0.665972900390625 +0.172375 0.1142578125 0.34222412109375 0.665972900390625 +0.17250000000000002 0.1142578125 0.34222412109375 0.665972900390625 +0.172625 0.113006591796875 0.33837890625 0.665972900390625 +0.17275000000000002 0.113006591796875 0.33837890625 0.665972900390625 +0.172875 0.1116943359375 0.334503173828125 0.665972900390625 +0.17300000000000002 0.110382080078125 0.330596923828125 0.665972900390625 +0.173125 0.110382080078125 0.330596923828125 0.665972900390625 +0.17325000000000002 0.10906982421875 0.32666015625 0.665972900390625 +0.173375 0.10906982421875 0.32666015625 0.665972900390625 +0.17350000000000002 0.107757568359375 0.322662353515625 0.665972900390625 +0.173625 0.106414794921875 0.31866455078125 0.665972900390625 +0.17375000000000002 0.106414794921875 0.31866455078125 0.665972900390625 +0.173875 0.10504150390625 0.314605712890625 0.665972900390625 +0.17400000000000002 0.10504150390625 0.314605712890625 0.665972900390625 +0.174125 0.10369873046875 0.310516357421875 0.665972900390625 +0.17425000000000002 0.102325439453125 0.306396484375 0.665972900390625 +0.174375 0.102325439453125 0.306396484375 0.665972900390625 +0.17450000000000002 0.100921630859375 0.30224609375 0.665972900390625 +0.174625 0.100921630859375 0.30224609375 0.665972900390625 +0.17475000000000002 0.099517822265625 0.298065185546875 0.665972900390625 +0.174875 0.098114013671875 0.2938232421875 0.665972900390625 +0.17500000000000002 0.098114013671875 0.2938232421875 0.665972900390625 +0.175125 0.096710205078125 0.289581298828125 0.665972900390625 +0.17525000000000002 0.096710205078125 0.289581298828125 0.665972900390625 +0.175375 0.09527587890625 0.285308837890625 0.665972900390625 +0.17550000000000002 0.09381103515625 0.280975341796875 0.665972900390625 +0.175625 0.09381103515625 0.280975341796875 0.665972900390625 +0.17575000000000002 0.092376708984375 0.276641845703125 0.665972900390625 +0.175875 0.092376708984375 0.276641845703125 0.665972900390625 +0.176 0.090911865234375 0.27227783203125 0.665972900390625 +0.176125 0.089447021484375 0.267852783203125 0.665972900390625 +0.17625 0.089447021484375 0.267852783203125 0.665972900390625 +0.176375 0.08795166015625 0.263427734375 0.665972900390625 +0.1765 0.08795166015625 0.263427734375 0.665972900390625 +0.176625 0.08648681640625 0.25897216796875 0.665972900390625 +0.17675 0.0849609375 0.25445556640625 0.665972900390625 +0.176875 0.0849609375 0.25445556640625 0.665972900390625 +0.177 0.083465576171875 0.249969482421875 0.665972900390625 +0.177125 0.083465576171875 0.249969482421875 0.665972900390625 +0.17725 0.081939697265625 0.24542236328125 0.665972900390625 +0.177375 0.080413818359375 0.2408447265625 0.665972900390625 +0.1775 0.080413818359375 0.2408447265625 0.665972900390625 +0.177625 0.078887939453125 0.236236572265625 0.665972900390625 +0.17775 0.078887939453125 0.236236572265625 0.665972900390625 +0.177875 0.07733154296875 0.231597900390625 0.665972900390625 +0.178 0.075775146484375 0.226959228515625 0.665972900390625 +0.178125 0.075775146484375 0.226959228515625 0.665972900390625 +0.17825 0.07421875 0.2222900390625 0.665972900390625 +0.178375 0.07421875 0.2222900390625 0.665972900390625 +0.1785 0.072662353515625 0.21759033203125 0.665972900390625 +0.178625 0.071075439453125 0.212860107421875 0.665972900390625 +0.17875 0.071075439453125 0.212860107421875 0.665972900390625 +0.178875 0.069488525390625 0.208099365234375 0.665972900390625 +0.179 0.069488525390625 0.208099365234375 0.665972900390625 +0.179125 0.067901611328125 0.203338623046875 0.665972900390625 +0.17925 0.0662841796875 0.19854736328125 0.665972900390625 +0.179375 0.0662841796875 0.19854736328125 0.665972900390625 +0.1795 0.064666748046875 0.1937255859375 0.665972900390625 +0.179625 0.064666748046875 0.1937255859375 0.665972900390625 +0.17975 0.06304931640625 0.188873291015625 0.665972900390625 +0.179875 0.061431884765625 0.18402099609375 0.665972900390625 +0.18 0.061431884765625 0.18402099609375 0.665972900390625 +0.180125 0.059814453125 0.17913818359375 0.665972900390625 +0.18025 0.059814453125 0.17913818359375 0.665972900390625 +0.180375 0.05816650390625 0.17425537109375 0.665972900390625 +0.1805 0.056549072265625 0.169342041015625 0.665972900390625 +0.180625 0.056549072265625 0.169342041015625 0.665972900390625 +0.18075 0.054901123046875 0.164398193359375 0.665972900390625 +0.180875 0.054901123046875 0.164398193359375 0.665972900390625 +0.181 0.05322265625 0.159454345703125 0.665972900390625 +0.181125 0.05157470703125 0.15447998046875 0.665972900390625 +0.18125 0.05157470703125 0.15447998046875 0.665972900390625 +0.181375 0.049896240234375 0.14947509765625 0.665972900390625 +0.1815 0.049896240234375 0.14947509765625 0.665972900390625 +0.181625 0.048248291015625 0.14447021484375 0.665972900390625 +0.18175 0.04656982421875 0.13946533203125 0.665972900390625 +0.181875 0.04656982421875 0.13946533203125 0.665972900390625 +0.182 0.044891357421875 0.134429931640625 0.665972900390625 +0.182125 0.044891357421875 0.134429931640625 0.665972900390625 +0.18225 0.043182373046875 0.129364013671875 0.665972900390625 +0.182375 0.04150390625 0.124298095703125 0.665972900390625 +0.1825 0.04150390625 0.124298095703125 0.665972900390625 +0.182625 0.039794921875 0.119232177734375 0.665972900390625 +0.18275 0.039794921875 0.119232177734375 0.665972900390625 +0.182875 0.038116455078125 0.1141357421875 0.665972900390625 +0.183 0.036407470703125 0.109039306640625 0.665972900390625 +0.183125 0.036407470703125 0.109039306640625 0.665972900390625 +0.18325 0.034698486328125 0.103912353515625 0.665972900390625 +0.183375 0.034698486328125 0.103912353515625 0.665972900390625 +0.1835 0.032989501953125 0.098785400390625 0.665972900390625 +0.183625 0.03125 0.093658447265625 0.665972900390625 +0.18375 0.03125 0.093658447265625 0.665972900390625 +0.183875 0.029541015625 0.0885009765625 0.665972900390625 +0.184 0.029541015625 0.0885009765625 0.665972900390625 +0.184125 0.02783203125 0.083343505859375 0.665972900390625 +0.18425 0.026092529296875 0.07818603515625 0.665972900390625 +0.184375 0.026092529296875 0.07818603515625 0.665972900390625 +0.1845 0.024383544921875 0.072998046875 0.665972900390625 +0.184625 0.024383544921875 0.072998046875 0.665972900390625 +0.18475 0.02264404296875 0.06781005859375 0.665972900390625 +0.184875 0.020904541015625 0.0626220703125 0.665972900390625 +0.185 0.020904541015625 0.0626220703125 0.665972900390625 +0.185125 0.0191650390625 0.05743408203125 0.665972900390625 +0.18525 0.0191650390625 0.05743408203125 0.665972900390625 +0.185375 0.017425537109375 0.05224609375 0.665972900390625 +0.1855 0.01568603515625 0.047027587890625 0.665972900390625 +0.185625 0.01568603515625 0.047027587890625 0.665972900390625 +0.18575 0.013946533203125 0.04180908203125 0.665972900390625 +0.185875 0.013946533203125 0.04180908203125 0.665972900390625 +0.186 0.01220703125 0.036590576171875 0.665972900390625 +0.186125 0.010467529296875 0.0313720703125 0.665972900390625 +0.18625 0.010467529296875 0.0313720703125 0.665972900390625 +0.186375 0.00872802734375 0.026123046875 0.665972900390625 +0.1865 0.00872802734375 0.026123046875 0.665972900390625 +0.186625 0.006988525390625 0.020904541015625 0.665972900390625 +0.18675 0.005218505859375 0.01568603515625 0.665972900390625 +0.186875 0.005218505859375 0.01568603515625 0.665972900390625 +0.187 0.00347900390625 0.01043701171875 0.665972900390625 +0.187125 0.00347900390625 0.01043701171875 0.665972900390625 +0.18725 0.001739501953125 0.005218505859375 0.665972900390625 +0.187375 0.0 0.0 0.665972900390625 +0.1875 0.0 0.0 0.665972900390625 +0.1876250000000000256 -0.001739501953125 -0.005218505859375 0.665972900390625 +0.18775 -0.001739501953125 -0.005218505859375 0.665972900390625 +0.1878750000000000256 -0.00347900390625 -0.01043701171875 0.665972900390625 +0.188 -0.005218505859375 -0.01568603515625 0.665972900390625 +0.1881250000000000256 -0.005218505859375 -0.01568603515625 0.665972900390625 +0.18825 -0.006988525390625 -0.020904541015625 0.665972900390625 +0.1883750000000000256 -0.006988525390625 -0.020904541015625 0.665972900390625 +0.1885 -0.00872802734375 -0.026123046875 0.665972900390625 +0.1886250000000000256 -0.010467529296875 -0.0313720703125 0.665972900390625 +0.18875 -0.010467529296875 -0.0313720703125 0.665972900390625 +0.1888750000000000256 -0.01220703125 -0.036590576171875 0.665972900390625 +0.189 -0.01220703125 -0.036590576171875 0.665972900390625 +0.1891250000000000256 -0.013946533203125 -0.04180908203125 0.665972900390625 +0.18925 -0.01568603515625 -0.047027587890625 0.665972900390625 +0.1893750000000000256 -0.01568603515625 -0.047027587890625 0.665972900390625 +0.1895 -0.017425537109375 -0.05224609375 0.665972900390625 +0.1896250000000000256 -0.017425537109375 -0.05224609375 0.665972900390625 +0.18975 -0.0191650390625 -0.05743408203125 0.665972900390625 +0.1898750000000000256 -0.020904541015625 -0.0626220703125 0.665972900390625 +0.19 -0.020904541015625 -0.0626220703125 0.665972900390625 +0.1901250000000000256 -0.02264404296875 -0.06781005859375 0.665972900390625 +0.19025 -0.02264404296875 -0.06781005859375 0.665972900390625 +0.1903750000000000256 -0.024383544921875 -0.072998046875 0.665972900390625 +0.1905 -0.026092529296875 -0.07818603515625 0.665972900390625 +0.1906250000000000256 -0.026092529296875 -0.07818603515625 0.665972900390625 +0.19075 -0.02783203125 -0.083343505859375 0.665972900390625 +0.1908750000000000256 -0.02783203125 -0.083343505859375 0.665972900390625 +0.191 -0.029541015625 -0.0885009765625 0.665972900390625 +0.1911250000000000256 -0.03125 -0.093658447265625 0.665972900390625 +0.19125 -0.03125 -0.093658447265625 0.665972900390625 +0.1913750000000000256 -0.032989501953125 -0.098785400390625 0.665972900390625 +0.1915 -0.032989501953125 -0.098785400390625 0.665972900390625 +0.1916250000000000256 -0.034698486328125 -0.103912353515625 0.665972900390625 +0.19175 -0.036407470703125 -0.109039306640625 0.665972900390625 +0.1918750000000000256 -0.036407470703125 -0.109039306640625 0.665972900390625 +0.192 -0.0775146484375 -0.1141357421875 0.3209167480468749 +0.192125 -0.0775146484375 -0.1141357421875 0.3209167480468749 +0.19225 -0.080963134765625 -0.119232177734375 0.3209167480468749 +0.192375 -0.08441162109375 -0.124298095703125 0.3209167480468749 +0.1925 -0.08441162109375 -0.124298095703125 0.3209167480468749 +0.192625 -0.08782958984375 -0.129364013671875 0.3209167480468749 +0.19275 -0.08782958984375 -0.129364013671875 0.3209167480468749 +0.192875 -0.091278076171875 -0.134429931640625 0.3209167480468749 +0.193 -0.094696044921875 -0.13946533203125 0.3209167480468749 +0.193125 -0.094696044921875 -0.13946533203125 0.3209167480468749 +0.19325 -0.09808349609375 -0.14447021484375 0.3209167480468749 +0.193375 -0.09808349609375 -0.14447021484375 0.3209167480468749 +0.1935 -0.10150146484375 -0.14947509765625 0.3209167480468749 +0.193625 -0.104888916015625 -0.15447998046875 0.3209167480468749 +0.19375 -0.104888916015625 -0.15447998046875 0.3209167480468749 +0.193875 -0.1082763671875 -0.159454345703125 0.3209167480468749 +0.194 -0.1082763671875 -0.159454345703125 0.3209167480468749 +0.194125 -0.11163330078125 -0.164398193359375 0.3209167480468749 +0.19425 -0.114990234375 -0.169342041015625 0.3209167480468749 +0.194375 -0.114990234375 -0.169342041015625 0.3209167480468749 +0.1945 -0.118316650390625 -0.17425537109375 0.3209167480468749 +0.194625 -0.118316650390625 -0.17425537109375 0.3209167480468749 +0.19475 -0.12164306640625 -0.17913818359375 0.3209167480468749 +0.194875 -0.12493896484375 -0.18402099609375 0.3209167480468749 +0.195 -0.12493896484375 -0.18402099609375 0.3209167480468749 +0.195125 -0.12823486328125 -0.188873291015625 0.3209167480468749 +0.19525 -0.12823486328125 -0.188873291015625 0.3209167480468749 +0.195375 -0.13153076171875 -0.1937255859375 0.3209167480468749 +0.1955 -0.134796142578125 -0.19854736328125 0.3209167480468749 +0.195625 -0.134796142578125 -0.19854736328125 0.3209167480468749 +0.19575 -0.1380615234375 -0.203338623046875 0.3209167480468749 +0.195875 -0.1380615234375 -0.203338623046875 0.3209167480468749 +0.196 -0.14129638671875 -0.208099365234375 0.3209167480468749 +0.196125 -0.14453125 -0.212860107421875 0.3209167480468749 +0.19625 -0.14453125 -0.212860107421875 0.3209167480468749 +0.196375 -0.147735595703125 -0.21759033203125 0.3209167480468749 +0.1965 -0.147735595703125 -0.21759033203125 0.3209167480468749 +0.196625 -0.15093994140625 -0.2222900390625 0.3209167480468749 +0.19675 -0.154083251953125 -0.226959228515625 0.3209167480468749 +0.196875 -0.154083251953125 -0.226959228515625 0.3209167480468749 +0.197 -0.157257080078125 -0.231597900390625 0.3209167480468749 +0.197125 -0.157257080078125 -0.231597900390625 0.3209167480468749 +0.19725 -0.160400390625 -0.236236572265625 0.3209167480468749 +0.197375 -0.16351318359375 -0.2408447265625 0.3209167480468749 +0.1975 -0.16351318359375 -0.2408447265625 0.3209167480468749 +0.197625 -0.1666259765625 -0.24542236328125 0.3209167480468749 +0.19775 -0.1666259765625 -0.24542236328125 0.3209167480468749 +0.197875 -0.169708251953125 -0.249969482421875 0.3209167480468749 +0.198 -0.17279052734375 -0.25445556640625 0.3209167480468749 +0.198125 -0.17279052734375 -0.25445556640625 0.3209167480468749 +0.19825 -0.17584228515625 -0.25897216796875 0.3209167480468749 +0.198375 -0.17584228515625 -0.25897216796875 0.3209167480468749 +0.1985 -0.178863525390625 -0.263427734375 0.3209167480468749 +0.198625 -0.181884765625 -0.267852783203125 0.3209167480468749 +0.19875 -0.181884765625 -0.267852783203125 0.3209167480468749 +0.198875 -0.18487548828125 -0.27227783203125 0.3209167480468749 +0.199 -0.18487548828125 -0.27227783203125 0.3209167480468749 +0.199125 -0.187835693359375 -0.276641845703125 0.3209167480468749 +0.19925 -0.1907958984375 -0.280975341796875 0.3209167480468749 +0.199375 -0.1907958984375 -0.280975341796875 0.3209167480468749 +0.1995 -0.1937255859375 -0.285308837890625 0.3209167480468749 +0.199625 -0.1937255859375 -0.285308837890625 0.3209167480468749 +0.19975 -0.196624755859375 -0.289581298828125 0.3209167480468749 +0.199875 -0.19952392578125 -0.2938232421875 0.3209167480468749 +0.2 -0.19952392578125 -0.2938232421875 0.3209167480468749 +0.200125 -0.202392578125 -0.298065185546875 0.3209167480468749 +0.20025 -0.202392578125 -0.298065185546875 0.3209167480468749 +0.200375 -0.205230712890625 -0.30224609375 0.3209167480468749 +0.2005 -0.20806884765625 -0.306396484375 0.3209167480468749 +0.200625 -0.20806884765625 -0.306396484375 0.3209167480468749 +0.20075 -0.210845947265625 -0.310516357421875 0.3209167480468749 +0.200875 -0.210845947265625 -0.310516357421875 0.3209167480468749 +0.201 -0.213623046875 -0.314605712890625 0.3209167480468749 +0.201125 -0.21636962890625 -0.31866455078125 0.3209167480468749 +0.20125 -0.21636962890625 -0.31866455078125 0.3209167480468749 +0.201375 -0.219085693359375 -0.322662353515625 0.3209167480468749 +0.2015 -0.219085693359375 -0.322662353515625 0.3209167480468749 +0.201625 -0.2218017578125 -0.32666015625 0.3209167480468749 +0.20175 -0.2244873046875 -0.330596923828125 0.3209167480468749 +0.201875 -0.2244873046875 -0.330596923828125 0.3209167480468749 +0.202 -0.227142333984375 -0.334503173828125 0.3209167480468749 +0.202125 -0.227142333984375 -0.334503173828125 0.3209167480468749 +0.20225 -0.229766845703125 -0.33837890625 0.3209167480468749 +0.202375 -0.23236083984375 -0.34222412109375 0.3209167480468749 +0.2025 -0.23236083984375 -0.34222412109375 0.3209167480468749 +0.202625 -0.234954833984375 -0.34600830078125 0.3209167480468749 +0.20275 -0.234954833984375 -0.34600830078125 0.3209167480468749 +0.202875 -0.23748779296875 -0.349761962890625 0.3209167480468749 +0.203 -0.240020751953125 -0.353485107421875 0.3209167480468749 +0.203125 -0.240020751953125 -0.353485107421875 0.3209167480468749 +0.2032500000000000256 -0.242523193359375 -0.357177734375 0.3209167480468749 +0.203375 -0.242523193359375 -0.357177734375 0.3209167480468749 +0.2035000000000000256 -0.2449951171875 -0.360809326171875 0.3209167480468749 +0.203625 -0.247467041015625 -0.36444091796875 0.3209167480468749 +0.2037500000000000256 -0.247467041015625 -0.36444091796875 0.3209167480468749 +0.203875 -0.2498779296875 -0.36798095703125 0.3209167480468749 +0.2040000000000000256 -0.2498779296875 -0.36798095703125 0.3209167480468749 +0.204125 -0.25225830078125 -0.37152099609375 0.3209167480468749 +0.2042500000000000256 -0.254638671875 -0.375 0.3209167480468749 +0.204375 -0.254638671875 -0.375 0.3209167480468749 +0.2045000000000000256 -0.2569580078125 -0.378448486328125 0.3209167480468749 +0.204625 -0.2569580078125 -0.378448486328125 0.3209167480468749 +0.2047500000000000256 -0.25927734375 -0.3818359375 0.3209167480468749 +0.204875 -0.261566162109375 -0.38519287109375 0.3209167480468749 +0.2050000000000000256 -0.261566162109375 -0.38519287109375 0.3209167480468749 +0.205125 -0.2637939453125 -0.388519287109375 0.3209167480468749 +0.2052500000000000256 -0.2637939453125 -0.388519287109375 0.3209167480468749 +0.205375 -0.266021728515625 -0.39178466796875 0.3209167480468749 +0.2055000000000000256 -0.268218994140625 -0.39501953125 0.3209167480468749 +0.205625 -0.268218994140625 -0.39501953125 0.3209167480468749 +0.2057500000000000256 -0.2703857421875 -0.398193359375 0.3209167480468749 +0.205875 -0.2703857421875 -0.398193359375 0.3209167480468749 +0.2060000000000000256 -0.27252197265625 -0.401336669921875 0.3209167480468749 +0.206125 -0.274627685546875 -0.404449462890625 0.3209167480468749 +0.2062500000000000256 -0.274627685546875 -0.404449462890625 0.3209167480468749 +0.206375 -0.276702880859375 -0.407501220703125 0.3209167480468749 +0.2065000000000000256 -0.276702880859375 -0.407501220703125 0.3209167480468749 +0.206625 -0.27874755859375 -0.4105224609375 0.3209167480468749 +0.2067500000000000256 -0.28076171875 -0.413482666015625 0.3209167480468749 +0.206875 -0.28076171875 -0.413482666015625 0.3209167480468749 +0.2070000000000000256 -0.282745361328125 -0.416412353515625 0.3209167480468749 +0.207125 -0.282745361328125 -0.416412353515625 0.3209167480468749 +0.2072500000000000256 -0.284698486328125 -0.419281005859375 0.3209167480468749 +0.207375 -0.28662109375 -0.422119140625 0.3209167480468749 +0.2075000000000000256 -0.28662109375 -0.422119140625 0.3209167480468749 +0.207625 -0.28851318359375 -0.424896240234375 0.3209167480468749 +0.2077500000000000256 -0.28851318359375 -0.424896240234375 0.3209167480468749 +0.207875 -0.29034423828125 -0.4276123046875 0.3209167480468749 +0.208000000000000032 -0.29217529296875 -0.4302978515625 0.3209167480468749 +0.208125 -0.29217529296875 -0.4302978515625 0.3209167480468749 +0.20825 -0.293975830078125 -0.432952880859375 0.3209167480468749 +0.208375 -0.293975830078125 -0.432952880859375 0.3209167480468749 +0.2085 -0.295745849609375 -0.435546875 0.3209167480468749 +0.208625 -0.297454833984375 -0.438079833984375 0.3209167480468749 +0.20875 -0.297454833984375 -0.438079833984375 0.3209167480468749 +0.208875 -0.299163818359375 -0.440582275390625 0.3209167480468749 +0.209 -0.299163818359375 -0.440582275390625 0.3209167480468749 +0.209125 -0.30084228515625 -0.44305419921875 0.3209167480468749 +0.20925 -0.302459716796875 -0.4454345703125 0.3209167480468749 +0.209375 -0.302459716796875 -0.4454345703125 0.3209167480468749 +0.2095 -0.304046630859375 -0.447784423828125 0.3209167480468749 +0.209625 -0.304046630859375 -0.447784423828125 0.3209167480468749 +0.20975 -0.305633544921875 -0.450103759765625 0.3209167480468749 +0.209875 -0.307159423828125 -0.452362060546875 0.3209167480468749 +0.21 -0.307159423828125 -0.452362060546875 0.3209167480468749 +0.210125 -0.30865478515625 -0.454559326171875 0.3209167480468749 +0.21025 -0.30865478515625 -0.454559326171875 0.3209167480468749 +0.210375 -0.31011962890625 -0.45672607421875 0.3209167480468749 +0.2105 -0.311553955078125 -0.458831787109375 0.3209167480468749 +0.210625 -0.311553955078125 -0.458831787109375 0.3209167480468749 +0.21075 -0.31292724609375 -0.46087646484375 0.3209167480468749 +0.210875 -0.31292724609375 -0.46087646484375 0.3209167480468749 +0.211 -0.314300537109375 -0.462890625 0.3209167480468749 +0.211125 -0.31561279296875 -0.464813232421875 0.3209167480468749 +0.21125 -0.31561279296875 -0.464813232421875 0.3209167480468749 +0.211375 -0.316925048828125 -0.46673583984375 0.3209167480468749 +0.2115 -0.316925048828125 -0.46673583984375 0.3209167480468749 +0.211625 -0.31817626953125 -0.46856689453125 0.3209167480468749 +0.21175 -0.31939697265625 -0.470367431640625 0.3209167480468749 +0.211875 -0.31939697265625 -0.470367431640625 0.3209167480468749 +0.212 -0.320587158203125 -0.472137451171875 0.3209167480468749 +0.212125 -0.320587158203125 -0.472137451171875 0.3209167480468749 +0.21225 -0.32171630859375 -0.47381591796875 0.3209167480468749 +0.212375 -0.322845458984375 -0.4754638671875 0.3209167480468749 +0.2125 -0.322845458984375 -0.4754638671875 0.3209167480468749 +0.212625 -0.32391357421875 -0.47705078125 0.3209167480468749 +0.21275 -0.32391357421875 -0.47705078125 0.3209167480468749 +0.212875 -0.324981689453125 -0.478607177734375 0.3209167480468749 +0.213 -0.32598876953125 -0.480072021484375 0.3209167480468749 +0.213125 -0.32598876953125 -0.480072021484375 0.3209167480468749 +0.21325 -0.32696533203125 -0.48150634765625 0.3209167480468749 +0.213375 -0.32696533203125 -0.48150634765625 0.3209167480468749 +0.2135 -0.327880859375 -0.48291015625 0.3209167480468749 +0.213625 -0.32879638671875 -0.484222412109375 0.3209167480468749 +0.21375 -0.32879638671875 -0.484222412109375 0.3209167480468749 +0.213875 -0.329681396484375 -0.485504150390625 0.3209167480468749 +0.214 -0.329681396484375 -0.485504150390625 0.3209167480468749 +0.214125 -0.33050537109375 -0.486724853515625 0.3209167480468749 +0.21425 -0.331268310546875 -0.487884521484375 0.3209167480468749 +0.214375 -0.331268310546875 -0.487884521484375 0.3209167480468749 +0.2145 -0.33203125 -0.489013671875 0.3209167480468749 +0.214625 -0.33203125 -0.489013671875 0.3209167480468749 +0.21475 -0.332763671875 -0.490081787109375 0.3209167480468749 +0.214875 -0.33343505859375 -0.4910888671875 0.3209167480468749 +0.215 -0.33343505859375 -0.4910888671875 0.3209167480468749 +0.215125 -0.3341064453125 -0.492034912109375 0.3209167480468749 +0.21525 -0.3341064453125 -0.492034912109375 0.3209167480468749 +0.215375 -0.334716796875 -0.492950439453125 0.3209167480468749 +0.2155 -0.335296630859375 -0.4937744140625 0.3209167480468749 +0.215625 -0.335296630859375 -0.4937744140625 0.3209167480468749 +0.21575 -0.3358154296875 -0.49456787109375 0.3209167480468749 +0.215875 -0.3358154296875 -0.49456787109375 0.3209167480468749 +0.216 -0.3363037109375 -0.49530029296875 0.3209167480468749 +0.216125 -0.3367919921875 -0.496002197265625 0.3209167480468749 +0.21625 -0.3367919921875 -0.496002197265625 0.3209167480468749 +0.216375 -0.33721923828125 -0.496612548828125 0.3209167480468749 +0.2165 -0.33721923828125 -0.496612548828125 0.3209167480468749 +0.216625 -0.337615966796875 -0.4971923828125 0.3209167480468749 +0.21675 -0.33795166015625 -0.497711181640625 0.3209167480468749 +0.216875 -0.33795166015625 -0.497711181640625 0.3209167480468749 +0.217 -0.338287353515625 -0.498199462890625 0.3209167480468749 +0.217125 -0.338287353515625 -0.498199462890625 0.3209167480468749 +0.21725 -0.33856201171875 -0.49859619140625 0.3209167480468749 +0.217375 -0.33880615234375 -0.49896240234375 0.3209167480468749 +0.2175 -0.33880615234375 -0.49896240234375 0.3209167480468749 +0.217625 -0.3389892578125 -0.499267578125 0.3209167480468749 +0.21775 -0.3389892578125 -0.499267578125 0.3209167480468749 +0.217875 -0.33917236328125 -0.49951171875 0.3209167480468749 +0.218 -0.33929443359375 -0.49969482421875 0.3209167480468749 +0.218125 -0.33929443359375 -0.49969482421875 0.3209167480468749 +0.21825 -0.339385986328125 -0.49981689453125 0.3209167480468749 +0.218375 -0.339385986328125 -0.49981689453125 0.3209167480468749 +0.2185 -0.339447021484375 -0.499908447265625 0.3209167480468749 +0.218625 -0.3394775390625 -0.49993896484375 0.3209167480468749 +0.21875 -0.3394775390625 -0.49993896484375 0.3209167480468749 +0.2188750000000000256 -0.339447021484375 -0.499908447265625 0.3209167480468749 +0.219 -0.339447021484375 -0.499908447265625 0.3209167480468749 +0.2191250000000000256 -0.339385986328125 -0.49981689453125 0.3209167480468749 +0.21925 -0.33929443359375 -0.49969482421875 0.3209167480468749 +0.2193750000000000256 -0.33929443359375 -0.49969482421875 0.3209167480468749 +0.2195 -0.33917236328125 -0.49951171875 0.3209167480468749 +0.2196250000000000256 -0.33917236328125 -0.49951171875 0.3209167480468749 +0.21975 -0.3389892578125 -0.499267578125 0.3209167480468749 +0.2198750000000000256 -0.33880615234375 -0.49896240234375 0.3209167480468749 +0.22 -0.33880615234375 -0.49896240234375 0.3209167480468749 +0.2201250000000000256 -0.33856201171875 -0.49859619140625 0.3209167480468749 +0.22025 -0.33856201171875 -0.49859619140625 0.3209167480468749 +0.2203750000000000256 -0.338287353515625 -0.498199462890625 0.3209167480468749 +0.2205 -0.33795166015625 -0.497711181640625 0.3209167480468749 +0.2206250000000000256 -0.33795166015625 -0.497711181640625 0.3209167480468749 +0.22075 -0.337615966796875 -0.4971923828125 0.3209167480468749 +0.2208750000000000256 -0.337615966796875 -0.4971923828125 0.3209167480468749 +0.221 -0.33721923828125 -0.496612548828125 0.3209167480468749 +0.2211250000000000256 -0.3367919921875 -0.496002197265625 0.3209167480468749 +0.22125 -0.3367919921875 -0.496002197265625 0.3209167480468749 +0.2213750000000000256 -0.3363037109375 -0.49530029296875 0.3209167480468749 +0.2215 -0.3363037109375 -0.49530029296875 0.3209167480468749 +0.2216250000000000256 -0.3358154296875 -0.49456787109375 0.3209167480468749 +0.22175 -0.335296630859375 -0.4937744140625 0.3209167480468749 +0.2218750000000000256 -0.335296630859375 -0.4937744140625 0.3209167480468749 +0.222 -0.334716796875 -0.492950439453125 0.3209167480468749 +0.2221250000000000256 -0.334716796875 -0.492950439453125 0.3209167480468749 +0.22225 -0.3341064453125 -0.492034912109375 0.3209167480468749 +0.2223750000000000256 -0.33343505859375 -0.4910888671875 0.3209167480468749 +0.2225 -0.33343505859375 -0.4910888671875 0.3209167480468749 +0.2226250000000000256 -0.332763671875 -0.490081787109375 0.3209167480468749 +0.22275 -0.332763671875 -0.490081787109375 0.3209167480468749 +0.2228750000000000256 -0.33203125 -0.489013671875 0.3209167480468749 +0.223 -0.331268310546875 -0.487884521484375 0.3209167480468749 +0.2231250000000000256 -0.331268310546875 -0.487884521484375 0.3209167480468749 +0.22325 -0.33050537109375 -0.486724853515625 0.3209167480468749 +0.2233750000000000256 -0.33050537109375 -0.486724853515625 0.3209167480468749 +0.2235 -0.329681396484375 -0.485504150390625 0.3209167480468749 +0.223625000000000032 -0.32879638671875 -0.484222412109375 0.3209167480468749 +0.22375 -0.32879638671875 -0.484222412109375 0.3209167480468749 +0.223875000000000032 -0.327880859375 -0.48291015625 0.3209167480468749 +0.224 -0.48291015625 -0.446533203125 -0.07529907226562488 +0.224125 -0.48150634765625 -0.44525146484375 -0.07529907226562488 +0.22425 -0.480072021484375 -0.44390869140625 -0.07529907226562488 +0.224375 -0.480072021484375 -0.44390869140625 -0.07529907226562488 +0.2245 -0.478607177734375 -0.442535400390625 -0.07529907226562488 +0.224625 -0.478607177734375 -0.442535400390625 -0.07529907226562488 +0.22475 -0.47705078125 -0.441131591796875 -0.07529907226562488 +0.224875 -0.4754638671875 -0.43963623046875 -0.07529907226562488 +0.225 -0.4754638671875 -0.43963623046875 -0.07529907226562488 +0.225125 -0.47381591796875 -0.438140869140625 -0.07529907226562488 +0.22525 -0.47381591796875 -0.438140869140625 -0.07529907226562488 +0.225375 -0.472137451171875 -0.436553955078125 -0.07529907226562488 +0.2255 -0.470367431640625 -0.4349365234375 -0.07529907226562488 +0.225625 -0.470367431640625 -0.4349365234375 -0.07529907226562488 +0.22575 -0.46856689453125 -0.43328857421875 -0.07529907226562488 +0.225875 -0.46856689453125 -0.43328857421875 -0.07529907226562488 +0.226 -0.46673583984375 -0.43157958984375 -0.07529907226562488 +0.226125 -0.464813232421875 -0.4298095703125 -0.07529907226562488 +0.22625 -0.464813232421875 -0.4298095703125 -0.07529907226562488 +0.226375 -0.462890625 -0.428009033203125 -0.07529907226562488 +0.2265 -0.462890625 -0.428009033203125 -0.07529907226562488 +0.226625 -0.46087646484375 -0.4261474609375 -0.07529907226562488 +0.22675 -0.458831787109375 -0.42425537109375 -0.07529907226562488 +0.226875 -0.458831787109375 -0.42425537109375 -0.07529907226562488 +0.227 -0.45672607421875 -0.42230224609375 -0.07529907226562488 +0.227125 -0.45672607421875 -0.42230224609375 -0.07529907226562488 +0.22725 -0.454559326171875 -0.420318603515625 -0.07529907226562488 +0.227375 -0.452362060546875 -0.41827392578125 -0.07529907226562488 +0.2275 -0.452362060546875 -0.41827392578125 -0.07529907226562488 +0.227625 -0.450103759765625 -0.41619873046875 -0.07529907226562488 +0.22775 -0.450103759765625 -0.41619873046875 -0.07529907226562488 +0.227875 -0.447784423828125 -0.4140625 -0.07529907226562488 +0.228 -0.4454345703125 -0.411895751953125 -0.07529907226562488 +0.228125 -0.4454345703125 -0.411895751953125 -0.07529907226562488 +0.22825 -0.44305419921875 -0.40966796875 -0.07529907226562488 +0.228375 -0.44305419921875 -0.40966796875 -0.07529907226562488 +0.2285 -0.440582275390625 -0.40740966796875 -0.07529907226562488 +0.228625 -0.438079833984375 -0.40509033203125 -0.07529907226562488 +0.22875 -0.438079833984375 -0.40509033203125 -0.07529907226562488 +0.228875 -0.435546875 -0.402740478515625 -0.07529907226562488 +0.229 -0.435546875 -0.402740478515625 -0.07529907226562488 +0.229125 -0.432952880859375 -0.40032958984375 -0.07529907226562488 +0.22925 -0.4302978515625 -0.39788818359375 -0.07529907226562488 +0.229375 -0.4302978515625 -0.39788818359375 -0.07529907226562488 +0.2295 -0.4276123046875 -0.395416259765625 -0.07529907226562488 +0.229625 -0.4276123046875 -0.395416259765625 -0.07529907226562488 +0.22975 -0.424896240234375 -0.39288330078125 -0.07529907226562488 +0.229875 -0.422119140625 -0.39031982421875 -0.07529907226562488 +0.23 -0.422119140625 -0.39031982421875 -0.07529907226562488 +0.230125 -0.419281005859375 -0.3876953125 -0.07529907226562488 +0.23025 -0.419281005859375 -0.3876953125 -0.07529907226562488 +0.230375 -0.416412353515625 -0.385040283203125 -0.07529907226562488 +0.2305 -0.413482666015625 -0.38232421875 -0.07529907226562488 +0.230625 -0.413482666015625 -0.38232421875 -0.07529907226562488 +0.23075 -0.4105224609375 -0.37957763671875 -0.07529907226562488 +0.230875 -0.4105224609375 -0.37957763671875 -0.07529907226562488 +0.231 -0.407501220703125 -0.376800537109375 -0.07529907226562488 +0.231125 -0.404449462890625 -0.373992919921875 -0.07529907226562488 +0.23125 -0.404449462890625 -0.373992919921875 -0.07529907226562488 +0.231375 -0.401336669921875 -0.371124267578125 -0.07529907226562488 +0.2315 -0.401336669921875 -0.371124267578125 -0.07529907226562488 +0.231625 -0.398193359375 -0.368194580078125 -0.07529907226562488 +0.23175 -0.39501953125 -0.365264892578125 -0.07529907226562488 +0.231875 -0.39501953125 -0.365264892578125 -0.07529907226562488 +0.232 -0.39178466796875 -0.362274169921875 -0.07529907226562488 +0.232125 -0.39178466796875 -0.362274169921875 -0.07529907226562488 +0.23225 -0.388519287109375 -0.3592529296875 -0.07529907226562488 +0.232375 -0.38519287109375 -0.356170654296875 -0.07529907226562488 +0.2325 -0.38519287109375 -0.356170654296875 -0.07529907226562488 +0.232625 -0.3818359375 -0.35308837890625 -0.07529907226562488 +0.23275 -0.3818359375 -0.35308837890625 -0.07529907226562488 +0.232875 -0.378448486328125 -0.349945068359375 -0.07529907226562488 +0.233 -0.375 -0.34674072265625 -0.07529907226562488 +0.233125 -0.375 -0.34674072265625 -0.07529907226562488 +0.23325 -0.37152099609375 -0.343536376953125 -0.07529907226562488 +0.233375 -0.37152099609375 -0.343536376953125 -0.07529907226562488 +0.2335 -0.36798095703125 -0.34027099609375 -0.07529907226562488 +0.233625 -0.36444091796875 -0.33697509765625 -0.07529907226562488 +0.23375 -0.36444091796875 -0.33697509765625 -0.07529907226562488 +0.233875 -0.360809326171875 -0.333648681640625 -0.07529907226562488 +0.234 -0.360809326171875 -0.333648681640625 -0.07529907226562488 +0.234125 -0.357177734375 -0.330291748046875 -0.07529907226562488 +0.23425 -0.353485107421875 -0.326873779296875 -0.07529907226562488 +0.234375 -0.353485107421875 -0.326873779296875 -0.07529907226562488 +0.234500000000000026 -0.349761962890625 -0.32342529296875 -0.07529907226562488 +0.234625 -0.349761962890625 -0.32342529296875 -0.07529907226562488 +0.234750000000000026 -0.34600830078125 -0.3199462890625 -0.07529907226562488 +0.234875 -0.34222412109375 -0.316436767578125 -0.07529907226562488 +0.235000000000000026 -0.34222412109375 -0.316436767578125 -0.07529907226562488 +0.235125 -0.33837890625 -0.312896728515625 -0.07529907226562488 +0.235250000000000026 -0.33837890625 -0.312896728515625 -0.07529907226562488 +0.235375 -0.334503173828125 -0.309326171875 -0.07529907226562488 +0.235500000000000026 -0.330596923828125 -0.305694580078125 -0.07529907226562488 +0.235625 -0.330596923828125 -0.305694580078125 -0.07529907226562488 +0.235750000000000026 -0.32666015625 -0.30206298828125 -0.07529907226562488 +0.235875 -0.32666015625 -0.30206298828125 -0.07529907226562488 +0.236000000000000026 -0.322662353515625 -0.298370361328125 -0.07529907226562488 +0.236125 -0.31866455078125 -0.294647216796875 -0.07529907226562488 +0.236250000000000026 -0.31866455078125 -0.294647216796875 -0.07529907226562488 +0.236375 -0.314605712890625 -0.2908935546875 -0.07529907226562488 +0.236500000000000026 -0.314605712890625 -0.2908935546875 -0.07529907226562488 +0.236625 -0.310516357421875 -0.287139892578125 -0.07529907226562488 +0.236750000000000026 -0.306396484375 -0.2833251953125 -0.07529907226562488 +0.236875 -0.306396484375 -0.2833251953125 -0.07529907226562488 +0.237000000000000026 -0.30224609375 -0.27947998046875 -0.07529907226562488 +0.237125 -0.30224609375 -0.27947998046875 -0.07529907226562488 +0.237250000000000026 -0.298065185546875 -0.275604248046875 -0.07529907226562488 +0.237375 -0.2938232421875 -0.271697998046875 -0.07529907226562488 +0.237500000000000026 -0.2938232421875 -0.271697998046875 -0.07529907226562488 +0.237625 -0.289581298828125 -0.267791748046875 -0.07529907226562488 +0.237750000000000026 -0.289581298828125 -0.267791748046875 -0.07529907226562488 +0.237875 -0.285308837890625 -0.263824462890625 -0.07529907226562488 +0.238000000000000026 -0.280975341796875 -0.25982666015625 -0.07529907226562488 +0.238125 -0.280975341796875 -0.25982666015625 -0.07529907226562488 +0.238250000000000026 -0.276641845703125 -0.25579833984375 -0.07529907226562488 +0.238375 -0.276641845703125 -0.25579833984375 -0.07529907226562488 +0.238500000000000026 -0.27227783203125 -0.25177001953125 -0.07529907226562488 +0.238625 -0.267852783203125 -0.2476806640625 -0.07529907226562488 +0.238750000000000026 -0.267852783203125 -0.2476806640625 -0.07529907226562488 +0.238875 -0.263427734375 -0.24359130859375 -0.07529907226562488 +0.239000000000000026 -0.263427734375 -0.24359130859375 -0.07529907226562488 +0.239125 -0.25897216796875 -0.239471435546875 -0.07529907226562488 +0.239250000000000032 -0.25445556640625 -0.23529052734375 -0.07529907226562488 +0.239375 -0.25445556640625 -0.23529052734375 -0.07529907226562488 +0.239500000000000032 -0.249969482421875 -0.23114013671875 -0.07529907226562488 +0.239625 -0.249969482421875 -0.23114013671875 -0.07529907226562488 +0.239750000000000032 -0.24542236328125 -0.2269287109375 -0.07529907226562488 +0.239875 -0.2408447265625 -0.222686767578125 -0.07529907226562488 +0.24 -0.2408447265625 -0.222686767578125 -0.07529907226562488 +0.240125 -0.236236572265625 -0.21844482421875 -0.07529907226562488 +0.24025 -0.236236572265625 -0.21844482421875 -0.07529907226562488 +0.240375 -0.231597900390625 -0.214141845703125 -0.07529907226562488 +0.2405 -0.226959228515625 -0.2098388671875 -0.07529907226562488 +0.240625 -0.226959228515625 -0.2098388671875 -0.07529907226562488 +0.24075 -0.2222900390625 -0.205535888671875 -0.07529907226562488 +0.240875 -0.2222900390625 -0.205535888671875 -0.07529907226562488 +0.241 -0.21759033203125 -0.201202392578125 -0.07529907226562488 +0.241125 -0.212860107421875 -0.196807861328125 -0.07529907226562488 +0.24125 -0.212860107421875 -0.196807861328125 -0.07529907226562488 +0.241375 -0.208099365234375 -0.192413330078125 -0.07529907226562488 +0.2415 -0.208099365234375 -0.192413330078125 -0.07529907226562488 +0.241625 -0.203338623046875 -0.188018798828125 -0.07529907226562488 +0.24175 -0.19854736328125 -0.18359375 -0.07529907226562488 +0.241875 -0.19854736328125 -0.18359375 -0.07529907226562488 +0.242 -0.1937255859375 -0.179107666015625 -0.07529907226562488 +0.242125 -0.1937255859375 -0.179107666015625 -0.07529907226562488 +0.24225 -0.188873291015625 -0.174652099609375 -0.07529907226562488 +0.242375 -0.18402099609375 -0.170166015625 -0.07529907226562488 +0.2425 -0.18402099609375 -0.170166015625 -0.07529907226562488 +0.242625 -0.17913818359375 -0.1656494140625 -0.07529907226562488 +0.24275 -0.17913818359375 -0.1656494140625 -0.07529907226562488 +0.242875 -0.17425537109375 -0.1611328125 -0.07529907226562488 +0.243 -0.169342041015625 -0.156585693359375 -0.07529907226562488 +0.243125 -0.169342041015625 -0.156585693359375 -0.07529907226562488 +0.24325 -0.164398193359375 -0.152008056640625 -0.07529907226562488 +0.243375 -0.164398193359375 -0.152008056640625 -0.07529907226562488 +0.2435 -0.159454345703125 -0.147430419921875 -0.07529907226562488 +0.243625 -0.15447998046875 -0.142822265625 -0.07529907226562488 +0.24375 -0.15447998046875 -0.142822265625 -0.07529907226562488 +0.243875 -0.14947509765625 -0.138214111328125 -0.07529907226562488 +0.244 -0.14947509765625 -0.138214111328125 -0.07529907226562488 +0.244125 -0.14447021484375 -0.13360595703125 -0.07529907226562488 +0.24425 -0.13946533203125 -0.128936767578125 -0.07529907226562488 +0.244375 -0.13946533203125 -0.128936767578125 -0.07529907226562488 +0.2445 -0.134429931640625 -0.124298095703125 -0.07529907226562488 +0.244625 -0.134429931640625 -0.124298095703125 -0.07529907226562488 +0.24475 -0.129364013671875 -0.11962890625 -0.07529907226562488 +0.244875 -0.124298095703125 -0.11492919921875 -0.07529907226562488 +0.245 -0.124298095703125 -0.11492919921875 -0.07529907226562488 +0.245125 -0.119232177734375 -0.110260009765625 -0.07529907226562488 +0.24525 -0.119232177734375 -0.110260009765625 -0.07529907226562488 +0.245375 -0.1141357421875 -0.105560302734375 -0.07529907226562488 +0.2455 -0.109039306640625 -0.100830078125 -0.07529907226562488 +0.245625 -0.109039306640625 -0.100830078125 -0.07529907226562488 +0.24575 -0.103912353515625 -0.096099853515625 -0.07529907226562488 +0.245875 -0.103912353515625 -0.096099853515625 -0.07529907226562488 +0.246 -0.098785400390625 -0.091339111328125 -0.07529907226562488 +0.246125 -0.093658447265625 -0.08660888671875 -0.07529907226562488 +0.24625 -0.093658447265625 -0.08660888671875 -0.07529907226562488 +0.246375 -0.0885009765625 -0.08184814453125 -0.07529907226562488 +0.2465 -0.0885009765625 -0.08184814453125 -0.07529907226562488 +0.246625 -0.083343505859375 -0.07708740234375 -0.07529907226562488 +0.24675 -0.07818603515625 -0.072296142578125 -0.07529907226562488 +0.246875 -0.07818603515625 -0.072296142578125 -0.07529907226562488 +0.247 -0.072998046875 -0.0675048828125 -0.07529907226562488 +0.247125 -0.072998046875 -0.0675048828125 -0.07529907226562488 +0.24725 -0.06781005859375 -0.062713623046875 -0.07529907226562488 +0.247375 -0.0626220703125 -0.05792236328125 -0.07529907226562488 +0.2475 -0.0626220703125 -0.05792236328125 -0.07529907226562488 +0.247625 -0.05743408203125 -0.053131103515625 -0.07529907226562488 +0.24775 -0.05743408203125 -0.053131103515625 -0.07529907226562488 +0.247875 -0.05224609375 -0.048309326171875 -0.07529907226562488 +0.248 -0.047027587890625 -0.043487548828125 -0.07529907226562488 +0.248125 -0.047027587890625 -0.043487548828125 -0.07529907226562488 +0.24825 -0.04180908203125 -0.038665771484375 -0.07529907226562488 +0.248375 -0.04180908203125 -0.038665771484375 -0.07529907226562488 +0.2485 -0.036590576171875 -0.033843994140625 -0.07529907226562488 +0.248625 -0.0313720703125 -0.02899169921875 -0.07529907226562488 +0.24875 -0.0313720703125 -0.02899169921875 -0.07529907226562488 +0.248875 -0.026123046875 -0.024169921875 -0.07529907226562488 +0.249 -0.026123046875 -0.024169921875 -0.07529907226562488 +0.249125 -0.020904541015625 -0.01934814453125 -0.07529907226562488 +0.24925 -0.01568603515625 -0.014495849609375 -0.07529907226562488 +0.249375 -0.01568603515625 -0.014495849609375 -0.07529907226562488 +0.2495 -0.01043701171875 -0.009674072265625 -0.07529907226562488 +0.249625 -0.01043701171875 -0.009674072265625 -0.07529907226562488 +0.24975 -0.005218505859375 -0.00482177734375 -0.07529907226562488 +0.249875 0.0 0.0 -0.07529907226562488 +0.25 0.0 0.0 -0.07529907226562488 +0.250125 0.005218505859375 0.00482177734375 -0.07529907226562488 +0.25025000000000004 0.005218505859375 0.00482177734375 -0.07529907226562488 +0.250375 0.01043701171875 0.009674072265625 -0.07529907226562488 +0.2505 0.01568603515625 0.014495849609375 -0.07529907226562488 +0.250625 0.01568603515625 0.014495849609375 -0.07529907226562488 +0.25075000000000004 0.020904541015625 0.01934814453125 -0.07529907226562488 +0.250875 0.020904541015625 0.01934814453125 -0.07529907226562488 +0.251 0.026123046875 0.024169921875 -0.07529907226562488 +0.251125 0.0313720703125 0.02899169921875 -0.07529907226562488 +0.25125000000000004 0.0313720703125 0.02899169921875 -0.07529907226562488 +0.251375 0.036590576171875 0.033843994140625 -0.07529907226562488 +0.2515 0.036590576171875 0.033843994140625 -0.07529907226562488 +0.251625 0.04180908203125 0.038665771484375 -0.07529907226562488 +0.25175000000000004 0.047027587890625 0.043487548828125 -0.07529907226562488 +0.251875 0.047027587890625 0.043487548828125 -0.07529907226562488 +0.252 0.05224609375 0.048309326171875 -0.07529907226562488 +0.252125 0.05224609375 0.048309326171875 -0.07529907226562488 +0.25225000000000004 0.05743408203125 0.053131103515625 -0.07529907226562488 +0.252375 0.0626220703125 0.05792236328125 -0.07529907226562488 +0.2525 0.0626220703125 0.05792236328125 -0.07529907226562488 +0.252625 0.06781005859375 0.062713623046875 -0.07529907226562488 +0.25275000000000004 0.06781005859375 0.062713623046875 -0.07529907226562488 +0.252875 0.072998046875 0.0675048828125 -0.07529907226562488 +0.253 0.07818603515625 0.072296142578125 -0.07529907226562488 +0.253125 0.07818603515625 0.072296142578125 -0.07529907226562488 +0.25325000000000004 0.083343505859375 0.07708740234375 -0.07529907226562488 +0.253375 0.083343505859375 0.07708740234375 -0.07529907226562488 +0.2535 0.0885009765625 0.08184814453125 -0.07529907226562488 +0.253625 0.093658447265625 0.08660888671875 -0.07529907226562488 +0.25375000000000004 0.093658447265625 0.08660888671875 -0.07529907226562488 +0.253875 0.098785400390625 0.091339111328125 -0.07529907226562488 +0.254 0.098785400390625 0.091339111328125 -0.07529907226562488 +0.254125 0.103912353515625 0.096099853515625 -0.07529907226562488 +0.25425000000000004 0.109039306640625 0.100830078125 -0.07529907226562488 +0.254375 0.109039306640625 0.100830078125 -0.07529907226562488 +0.2545 0.1141357421875 0.105560302734375 -0.07529907226562488 +0.254625 0.1141357421875 0.105560302734375 -0.07529907226562488 +0.25475000000000004 0.119232177734375 0.110260009765625 -0.07529907226562488 +0.254875 0.124298095703125 0.11492919921875 -0.07529907226562488 +0.255 0.124298095703125 0.11492919921875 -0.07529907226562488 +0.255125 0.129364013671875 0.11962890625 -0.07529907226562488 +0.25525000000000004 0.129364013671875 0.11962890625 -0.07529907226562488 +0.255375 0.134429931640625 0.124298095703125 -0.07529907226562488 +0.2555 0.13946533203125 0.128936767578125 -0.07529907226562488 +0.255625 0.13946533203125 0.128936767578125 -0.07529907226562488 +0.25575000000000004 0.14447021484375 0.13360595703125 -0.07529907226562488 +0.255875 0.14447021484375 0.13360595703125 -0.07529907226562488 +0.256 0.14947509765625 0.080780029296875 -0.4595336914062502 +0.256125 0.15447998046875 0.083465576171875 -0.4595336914062502 +0.25625 0.15447998046875 0.083465576171875 -0.4595336914062502 +0.256375 0.159454345703125 0.086151123046875 -0.4595336914062502 +0.2565 0.159454345703125 0.086151123046875 -0.4595336914062502 +0.256625 0.164398193359375 0.088836669921875 -0.4595336914062502 +0.25675 0.169342041015625 0.09149169921875 -0.4595336914062502 +0.256875 0.169342041015625 0.09149169921875 -0.4595336914062502 +0.257 0.17425537109375 0.094146728515625 -0.4595336914062502 +0.257125 0.17425537109375 0.094146728515625 -0.4595336914062502 +0.25725 0.17913818359375 0.0968017578125 -0.4595336914062502 +0.257375 0.18402099609375 0.099456787109375 -0.4595336914062502 +0.2575 0.18402099609375 0.099456787109375 -0.4595336914062502 +0.257625 0.188873291015625 0.10205078125 -0.4595336914062502 +0.25775 0.188873291015625 0.10205078125 -0.4595336914062502 +0.257875 0.1937255859375 0.10467529296875 -0.4595336914062502 +0.258 0.19854736328125 0.107269287109375 -0.4595336914062502 +0.258125 0.19854736328125 0.107269287109375 -0.4595336914062502 +0.25825 0.203338623046875 0.10986328125 -0.4595336914062502 +0.258375 0.203338623046875 0.10986328125 -0.4595336914062502 +0.2585 0.208099365234375 0.112457275390625 -0.4595336914062502 +0.258625 0.212860107421875 0.115020751953125 -0.4595336914062502 +0.25875 0.212860107421875 0.115020751953125 -0.4595336914062502 +0.258875 0.21759033203125 0.117584228515625 -0.4595336914062502 +0.259 0.21759033203125 0.117584228515625 -0.4595336914062502 +0.259125 0.2222900390625 0.1201171875 -0.4595336914062502 +0.25925 0.226959228515625 0.122650146484375 -0.4595336914062502 +0.259375 0.226959228515625 0.122650146484375 -0.4595336914062502 +0.2595 0.231597900390625 0.125152587890625 -0.4595336914062502 +0.259625 0.231597900390625 0.125152587890625 -0.4595336914062502 +0.25975 0.236236572265625 0.127655029296875 -0.4595336914062502 +0.259875 0.2408447265625 0.130126953125 -0.4595336914062502 +0.26 0.2408447265625 0.130126953125 -0.4595336914062502 +0.260125 0.24542236328125 0.132598876953125 -0.4595336914062502 +0.26025 0.24542236328125 0.132598876953125 -0.4595336914062502 +0.260375 0.249969482421875 0.13507080078125 -0.4595336914062502 +0.2605 0.25445556640625 0.13751220703125 -0.4595336914062502 +0.260625 0.25445556640625 0.13751220703125 -0.4595336914062502 +0.26075 0.25897216796875 0.13995361328125 -0.4595336914062502 +0.260875 0.25897216796875 0.13995361328125 -0.4595336914062502 +0.261 0.263427734375 0.142364501953125 -0.4595336914062502 +0.261125 0.267852783203125 0.144744873046875 -0.4595336914062502 +0.26125 0.267852783203125 0.144744873046875 -0.4595336914062502 +0.261375 0.27227783203125 0.147125244140625 -0.4595336914062502 +0.2615 0.27227783203125 0.147125244140625 -0.4595336914062502 +0.261625 0.276641845703125 0.149505615234375 -0.4595336914062502 +0.26175 0.280975341796875 0.15185546875 -0.4595336914062502 +0.261875 0.280975341796875 0.15185546875 -0.4595336914062502 +0.262 0.285308837890625 0.1541748046875 -0.4595336914062502 +0.262125 0.285308837890625 0.1541748046875 -0.4595336914062502 +0.26225 0.289581298828125 0.156494140625 -0.4595336914062502 +0.262375 0.2938232421875 0.158782958984375 -0.4595336914062502 +0.2625 0.2938232421875 0.158782958984375 -0.4595336914062502 +0.262625 0.298065185546875 0.16107177734375 -0.4595336914062502 +0.26275 0.298065185546875 0.16107177734375 -0.4595336914062502 +0.262875 0.30224609375 0.163330078125 -0.4595336914062502 +0.263 0.306396484375 0.16558837890625 -0.4595336914062502 +0.263125 0.306396484375 0.16558837890625 -0.4595336914062502 +0.26325 0.310516357421875 0.167816162109375 -0.4595336914062502 +0.263375 0.310516357421875 0.167816162109375 -0.4595336914062502 +0.2635 0.314605712890625 0.170013427734375 -0.4595336914062502 +0.263625 0.31866455078125 0.172210693359375 -0.4595336914062502 +0.26375 0.31866455078125 0.172210693359375 -0.4595336914062502 +0.263875 0.322662353515625 0.17437744140625 -0.4595336914062502 +0.264 0.322662353515625 0.17437744140625 -0.4595336914062502 +0.264125 0.32666015625 0.176513671875 -0.4595336914062502 +0.26425 0.330596923828125 0.17864990234375 -0.4595336914062502 +0.264375 0.330596923828125 0.17864990234375 -0.4595336914062502 +0.2645 0.334503173828125 0.180755615234375 -0.4595336914062502 +0.264625 0.334503173828125 0.180755615234375 -0.4595336914062502 +0.26475 0.33837890625 0.182861328125 -0.4595336914062502 +0.264875 0.34222412109375 0.1849365234375 -0.4595336914062502 +0.265 0.34222412109375 0.1849365234375 -0.4595336914062502 +0.265125 0.34600830078125 0.186981201171875 -0.4595336914062502 +0.26525 0.34600830078125 0.186981201171875 -0.4595336914062502 +0.265375 0.349761962890625 0.18902587890625 -0.4595336914062502 +0.2655 0.353485107421875 0.191009521484375 -0.4595336914062502 +0.265625 0.353485107421875 0.191009521484375 -0.4595336914062502 +0.26575 0.357177734375 0.193023681640625 -0.4595336914062502 +0.26587500000000004 0.357177734375 0.193023681640625 -0.4595336914062502 +0.266 0.360809326171875 0.194976806640625 -0.4595336914062502 +0.266125 0.36444091796875 0.196929931640625 -0.4595336914062502 +0.26625 0.36444091796875 0.196929931640625 -0.4595336914062502 +0.26637500000000004 0.36798095703125 0.1988525390625 -0.4595336914062502 +0.2665 0.36798095703125 0.1988525390625 -0.4595336914062502 +0.266625 0.37152099609375 0.200775146484375 -0.4595336914062502 +0.26675 0.375 0.20263671875 -0.4595336914062502 +0.26687500000000004 0.375 0.20263671875 -0.4595336914062502 +0.267 0.378448486328125 0.204498291015625 -0.4595336914062502 +0.267125 0.378448486328125 0.204498291015625 -0.4595336914062502 +0.26725 0.3818359375 0.206329345703125 -0.4595336914062502 +0.26737500000000004 0.38519287109375 0.208160400390625 -0.4595336914062502 +0.2675 0.38519287109375 0.208160400390625 -0.4595336914062502 +0.267625 0.388519287109375 0.2099609375 -0.4595336914062502 +0.26775 0.388519287109375 0.2099609375 -0.4595336914062502 +0.26787500000000004 0.39178466796875 0.21173095703125 -0.4595336914062502 +0.268 0.39501953125 0.213470458984375 -0.4595336914062502 +0.268125 0.39501953125 0.213470458984375 -0.4595336914062502 +0.26825 0.398193359375 0.215179443359375 -0.4595336914062502 +0.26837500000000004 0.398193359375 0.215179443359375 -0.4595336914062502 +0.2685 0.401336669921875 0.216888427734375 -0.4595336914062502 +0.268625 0.404449462890625 0.21856689453125 -0.4595336914062502 +0.26875 0.404449462890625 0.21856689453125 -0.4595336914062502 +0.26887500000000004 0.407501220703125 0.22021484375 -0.4595336914062502 +0.269 0.407501220703125 0.22021484375 -0.4595336914062502 +0.269125 0.4105224609375 0.221832275390625 -0.4595336914062502 +0.26925 0.413482666015625 0.22344970703125 -0.4595336914062502 +0.26937500000000004 0.413482666015625 0.22344970703125 -0.4595336914062502 +0.2695 0.416412353515625 0.225006103515625 -0.4595336914062502 +0.269625 0.416412353515625 0.225006103515625 -0.4595336914062502 +0.26975 0.419281005859375 0.2265625 -0.4595336914062502 +0.26987500000000004 0.422119140625 0.228118896484375 -0.4595336914062502 +0.27 0.422119140625 0.228118896484375 -0.4595336914062502 +0.270125 0.424896240234375 0.2296142578125 -0.4595336914062502 +0.27025 0.424896240234375 0.2296142578125 -0.4595336914062502 +0.27037500000000004 0.4276123046875 0.2310791015625 -0.4595336914062502 +0.2705 0.4302978515625 0.2325439453125 -0.4595336914062502 +0.270625 0.4302978515625 0.2325439453125 -0.4595336914062502 +0.27075 0.432952880859375 0.233978271484375 -0.4595336914062502 +0.27087500000000004 0.432952880859375 0.233978271484375 -0.4595336914062502 +0.271 0.435546875 0.235382080078125 -0.4595336914062502 +0.271125 0.438079833984375 0.236724853515625 -0.4595336914062502 +0.27125 0.438079833984375 0.236724853515625 -0.4595336914062502 +0.27137500000000004 0.440582275390625 0.23809814453125 -0.4595336914062502 +0.2715 0.440582275390625 0.23809814453125 -0.4595336914062502 +0.271625 0.44305419921875 0.239410400390625 -0.4595336914062502 +0.27175 0.4454345703125 0.24072265625 -0.4595336914062502 +0.27187500000000004 0.4454345703125 0.24072265625 -0.4595336914062502 +0.272 0.447784423828125 0.241973876953125 -0.4595336914062502 +0.272125 0.447784423828125 0.241973876953125 -0.4595336914062502 +0.27225 0.450103759765625 0.24322509765625 -0.4595336914062502 +0.272375 0.452362060546875 0.24444580078125 -0.4595336914062502 +0.2725 0.452362060546875 0.24444580078125 -0.4595336914062502 +0.272625 0.454559326171875 0.245635986328125 -0.4595336914062502 +0.27275 0.454559326171875 0.245635986328125 -0.4595336914062502 +0.272875 0.45672607421875 0.246795654296875 -0.4595336914062502 +0.273 0.458831787109375 0.247955322265625 -0.4595336914062502 +0.273125 0.458831787109375 0.247955322265625 -0.4595336914062502 +0.27325 0.46087646484375 0.249053955078125 -0.4595336914062502 +0.273375 0.46087646484375 0.249053955078125 -0.4595336914062502 +0.2735 0.462890625 0.2501220703125 -0.4595336914062502 +0.273625 0.464813232421875 0.251190185546875 -0.4595336914062502 +0.27375 0.464813232421875 0.251190185546875 -0.4595336914062502 +0.273875 0.46673583984375 0.252227783203125 -0.4595336914062502 +0.274 0.46673583984375 0.252227783203125 -0.4595336914062502 +0.274125 0.46856689453125 0.253204345703125 -0.4595336914062502 +0.27425 0.470367431640625 0.254180908203125 -0.4595336914062502 +0.274375 0.470367431640625 0.254180908203125 -0.4595336914062502 +0.2745 0.472137451171875 0.255126953125 -0.4595336914062502 +0.274625 0.472137451171875 0.255126953125 -0.4595336914062502 +0.27475 0.47381591796875 0.25604248046875 -0.4595336914062502 +0.274875 0.4754638671875 0.256927490234375 -0.4595336914062502 +0.275 0.4754638671875 0.256927490234375 -0.4595336914062502 +0.275125 0.47705078125 0.257781982421875 -0.4595336914062502 +0.27525 0.47705078125 0.257781982421875 -0.4595336914062502 +0.275375 0.478607177734375 0.258636474609375 -0.4595336914062502 +0.2755 0.480072021484375 0.259429931640625 -0.4595336914062502 +0.275625 0.480072021484375 0.259429931640625 -0.4595336914062502 +0.27575 0.48150634765625 0.26019287109375 -0.4595336914062502 +0.275875 0.48150634765625 0.26019287109375 -0.4595336914062502 +0.276 0.48291015625 0.260955810546875 -0.4595336914062502 +0.276125 0.484222412109375 0.26165771484375 -0.4595336914062502 +0.27625 0.484222412109375 0.26165771484375 -0.4595336914062502 +0.276375 0.485504150390625 0.262359619140625 -0.4595336914062502 +0.2765 0.485504150390625 0.262359619140625 -0.4595336914062502 +0.276625 0.486724853515625 0.263031005859375 -0.4595336914062502 +0.27675 0.487884521484375 0.263641357421875 -0.4595336914062502 +0.276875 0.487884521484375 0.263641357421875 -0.4595336914062502 +0.277 0.489013671875 0.264251708984375 -0.4595336914062502 +0.277125 0.489013671875 0.264251708984375 -0.4595336914062502 +0.27725 0.490081787109375 0.26483154296875 -0.4595336914062502 +0.277375 0.4910888671875 0.265380859375 -0.4595336914062502 +0.2775 0.4910888671875 0.265380859375 -0.4595336914062502 +0.277625 0.492034912109375 0.265899658203125 -0.4595336914062502 +0.27775 0.492034912109375 0.265899658203125 -0.4595336914062502 +0.277875 0.492950439453125 0.266387939453125 -0.4595336914062502 +0.278 0.4937744140625 0.266845703125 -0.4595336914062502 +0.278125 0.4937744140625 0.266845703125 -0.4595336914062502 +0.27825 0.49456787109375 0.26727294921875 -0.4595336914062502 +0.278375 0.49456787109375 0.26727294921875 -0.4595336914062502 +0.2785 0.49530029296875 0.267669677734375 -0.4595336914062502 +0.278625 0.496002197265625 0.268035888671875 -0.4595336914062502 +0.27875 0.496002197265625 0.268035888671875 -0.4595336914062502 +0.278875 0.496612548828125 0.26837158203125 -0.4595336914062502 +0.279 0.496612548828125 0.26837158203125 -0.4595336914062502 +0.279125 0.4971923828125 0.2686767578125 -0.4595336914062502 +0.27925 0.497711181640625 0.268951416015625 -0.4595336914062502 +0.279375 0.497711181640625 0.268951416015625 -0.4595336914062502 +0.2795 0.498199462890625 0.26922607421875 -0.4595336914062502 +0.279625 0.498199462890625 0.26922607421875 -0.4595336914062502 +0.27975 0.49859619140625 0.269439697265625 -0.4595336914062502 +0.279875 0.49896240234375 0.269622802734375 -0.4595336914062502 +0.28 0.49896240234375 0.269622802734375 -0.4595336914062502 +0.280125 0.499267578125 0.269805908203125 -0.4595336914062502 +0.28025 0.499267578125 0.269805908203125 -0.4595336914062502 +0.280375 0.49951171875 0.269927978515625 -0.4595336914062502 +0.2805 0.49969482421875 0.27001953125 -0.4595336914062502 +0.280625 0.49969482421875 0.27001953125 -0.4595336914062502 +0.28075 0.49981689453125 0.270111083984375 -0.4595336914062502 +0.280875 0.49981689453125 0.270111083984375 -0.4595336914062502 +0.281 0.499908447265625 0.2701416015625 -0.4595336914062502 +0.281125 0.49993896484375 0.270172119140625 -0.4595336914062502 +0.28125 0.49993896484375 0.270172119140625 -0.4595336914062502 +0.281375 0.499908447265625 0.2701416015625 -0.4595336914062502 +0.28150000000000004 0.499908447265625 0.2701416015625 -0.4595336914062502 +0.281625 0.49981689453125 0.270111083984375 -0.4595336914062502 +0.28175 0.49969482421875 0.27001953125 -0.4595336914062502 +0.281875 0.49969482421875 0.27001953125 -0.4595336914062502 +0.28200000000000004 0.49951171875 0.269927978515625 -0.4595336914062502 +0.282125 0.49951171875 0.269927978515625 -0.4595336914062502 +0.28225 0.499267578125 0.269805908203125 -0.4595336914062502 +0.282375 0.49896240234375 0.269622802734375 -0.4595336914062502 +0.28250000000000004 0.49896240234375 0.269622802734375 -0.4595336914062502 +0.282625 0.49859619140625 0.269439697265625 -0.4595336914062502 +0.28275 0.49859619140625 0.269439697265625 -0.4595336914062502 +0.282875 0.498199462890625 0.26922607421875 -0.4595336914062502 +0.28300000000000004 0.497711181640625 0.268951416015625 -0.4595336914062502 +0.283125 0.497711181640625 0.268951416015625 -0.4595336914062502 +0.28325 0.4971923828125 0.2686767578125 -0.4595336914062502 +0.283375 0.4971923828125 0.2686767578125 -0.4595336914062502 +0.28350000000000004 0.496612548828125 0.26837158203125 -0.4595336914062502 +0.283625 0.496002197265625 0.268035888671875 -0.4595336914062502 +0.28375 0.496002197265625 0.268035888671875 -0.4595336914062502 +0.283875 0.49530029296875 0.267669677734375 -0.4595336914062502 +0.28400000000000004 0.49530029296875 0.267669677734375 -0.4595336914062502 +0.284125 0.49456787109375 0.26727294921875 -0.4595336914062502 +0.28425 0.4937744140625 0.266845703125 -0.4595336914062502 +0.284375 0.4937744140625 0.266845703125 -0.4595336914062502 +0.28450000000000004 0.492950439453125 0.266387939453125 -0.4595336914062502 +0.284625 0.492950439453125 0.266387939453125 -0.4595336914062502 +0.28475 0.492034912109375 0.265899658203125 -0.4595336914062502 +0.284875 0.4910888671875 0.265380859375 -0.4595336914062502 +0.28500000000000004 0.4910888671875 0.265380859375 -0.4595336914062502 +0.285125 0.490081787109375 0.26483154296875 -0.4595336914062502 +0.28525 0.490081787109375 0.26483154296875 -0.4595336914062502 +0.285375 0.489013671875 0.264251708984375 -0.4595336914062502 +0.28550000000000004 0.487884521484375 0.263641357421875 -0.4595336914062502 +0.285625 0.487884521484375 0.263641357421875 -0.4595336914062502 +0.28575 0.486724853515625 0.263031005859375 -0.4595336914062502 +0.285875 0.486724853515625 0.263031005859375 -0.4595336914062502 +0.28600000000000004 0.485504150390625 0.262359619140625 -0.4595336914062502 +0.286125 0.484222412109375 0.26165771484375 -0.4595336914062502 +0.28625 0.484222412109375 0.26165771484375 -0.4595336914062502 +0.286375 0.48291015625 0.260955810546875 -0.4595336914062502 +0.28650000000000004 0.48291015625 0.260955810546875 -0.4595336914062502 +0.286625 0.48150634765625 0.26019287109375 -0.4595336914062502 +0.28675 0.480072021484375 0.259429931640625 -0.4595336914062502 +0.286875 0.480072021484375 0.259429931640625 -0.4595336914062502 +0.28700000000000004 0.478607177734375 0.258636474609375 -0.4595336914062502 +0.287125 0.478607177734375 0.258636474609375 -0.4595336914062502 +0.28725 0.47705078125 0.257781982421875 -0.4595336914062502 +0.287375 0.4754638671875 0.256927490234375 -0.4595336914062502 +0.28750000000000004 0.4754638671875 0.256927490234375 -0.4595336914062502 +0.287625 0.47381591796875 0.25604248046875 -0.4595336914062502 +0.28775 0.47381591796875 0.25604248046875 -0.4595336914062502 +0.287875 0.472137451171875 0.255126953125 -0.4595336914062502 +0.28800000000000004 0.470367431640625 0.10791015625 -0.7704772949218753 +0.288125 0.470367431640625 0.10791015625 -0.7704772949218753 +0.28825 0.46856689453125 0.107513427734375 -0.7704772949218753 +0.288375 0.46856689453125 0.107513427734375 -0.7704772949218753 +0.2885 0.46673583984375 0.107086181640625 -0.7704772949218753 +0.288625 0.464813232421875 0.10662841796875 -0.7704772949218753 +0.28875 0.464813232421875 0.10662841796875 -0.7704772949218753 +0.288875 0.462890625 0.106201171875 -0.7704772949218753 +0.289 0.462890625 0.106201171875 -0.7704772949218753 +0.289125 0.46087646484375 0.105743408203125 -0.7704772949218753 +0.28925 0.458831787109375 0.105255126953125 -0.7704772949218753 +0.289375 0.458831787109375 0.105255126953125 -0.7704772949218753 +0.2895 0.45672607421875 0.104766845703125 -0.7704772949218753 +0.289625 0.45672607421875 0.104766845703125 -0.7704772949218753 +0.28975 0.454559326171875 0.104278564453125 -0.7704772949218753 +0.289875 0.452362060546875 0.103790283203125 -0.7704772949218753 +0.29 0.452362060546875 0.103790283203125 -0.7704772949218753 +0.290125 0.450103759765625 0.103271484375 -0.7704772949218753 +0.29025 0.450103759765625 0.103271484375 -0.7704772949218753 +0.290375 0.447784423828125 0.10272216796875 -0.7704772949218753 +0.2905 0.4454345703125 0.102203369140625 -0.7704772949218753 +0.290625 0.4454345703125 0.102203369140625 -0.7704772949218753 +0.29075 0.44305419921875 0.101654052734375 -0.7704772949218753 +0.290875 0.44305419921875 0.101654052734375 -0.7704772949218753 +0.291 0.440582275390625 0.10107421875 -0.7704772949218753 +0.291125 0.438079833984375 0.100494384765625 -0.7704772949218753 +0.29125 0.438079833984375 0.100494384765625 -0.7704772949218753 +0.291375 0.435546875 0.09991455078125 -0.7704772949218753 +0.2915 0.435546875 0.09991455078125 -0.7704772949218753 +0.291625 0.432952880859375 0.099334716796875 -0.7704772949218753 +0.29175 0.4302978515625 0.098724365234375 -0.7704772949218753 +0.291875 0.4302978515625 0.098724365234375 -0.7704772949218753 +0.292 0.4276123046875 0.098114013671875 -0.7704772949218753 +0.292125 0.4276123046875 0.098114013671875 -0.7704772949218753 +0.29225 0.424896240234375 0.09747314453125 -0.7704772949218753 +0.292375 0.422119140625 0.096832275390625 -0.7704772949218753 +0.2925 0.422119140625 0.096832275390625 -0.7704772949218753 +0.292625 0.419281005859375 0.09619140625 -0.7704772949218753 +0.29275 0.419281005859375 0.09619140625 -0.7704772949218753 +0.292875 0.416412353515625 0.09552001953125 -0.7704772949218753 +0.293 0.413482666015625 0.0948486328125 -0.7704772949218753 +0.293125 0.413482666015625 0.0948486328125 -0.7704772949218753 +0.29325 0.4105224609375 0.09417724609375 -0.7704772949218753 +0.293375 0.4105224609375 0.09417724609375 -0.7704772949218753 +0.2935 0.407501220703125 0.093475341796875 -0.7704772949218753 +0.293625 0.404449462890625 0.0927734375 -0.7704772949218753 +0.29375 0.404449462890625 0.0927734375 -0.7704772949218753 +0.293875 0.401336669921875 0.092071533203125 -0.7704772949218753 +0.294 0.401336669921875 0.092071533203125 -0.7704772949218753 +0.294125 0.398193359375 0.091339111328125 -0.7704772949218753 +0.29425 0.39501953125 0.09063720703125 -0.7704772949218753 +0.294375 0.39501953125 0.09063720703125 -0.7704772949218753 +0.2945 0.39178466796875 0.089874267578125 -0.7704772949218753 +0.294625 0.39178466796875 0.089874267578125 -0.7704772949218753 +0.29475 0.388519287109375 0.089141845703125 -0.7704772949218753 +0.294875 0.38519287109375 0.08837890625 -0.7704772949218753 +0.295 0.38519287109375 0.08837890625 -0.7704772949218753 +0.295125 0.3818359375 0.08758544921875 -0.7704772949218753 +0.29525 0.3818359375 0.08758544921875 -0.7704772949218753 +0.295375 0.378448486328125 0.086822509765625 -0.7704772949218753 +0.2955 0.375 0.086029052734375 -0.7704772949218753 +0.295625 0.375 0.086029052734375 -0.7704772949218753 +0.29575 0.37152099609375 0.085235595703125 -0.7704772949218753 +0.295875 0.37152099609375 0.085235595703125 -0.7704772949218753 +0.296 0.36798095703125 0.08441162109375 -0.7704772949218753 +0.296125 0.36444091796875 0.0836181640625 -0.7704772949218753 +0.29625 0.36444091796875 0.0836181640625 -0.7704772949218753 +0.296375 0.360809326171875 0.082763671875 -0.7704772949218753 +0.2965 0.360809326171875 0.082763671875 -0.7704772949218753 +0.296625 0.357177734375 0.081939697265625 -0.7704772949218753 +0.29675 0.353485107421875 0.081085205078125 -0.7704772949218753 +0.296875 0.353485107421875 0.081085205078125 -0.7704772949218753 +0.297 0.349761962890625 0.080230712890625 -0.7704772949218753 +0.29712500000000004 0.349761962890625 0.080230712890625 -0.7704772949218753 +0.29725 0.34600830078125 0.079376220703125 -0.7704772949218753 +0.297375 0.34222412109375 0.078521728515625 -0.7704772949218753 +0.2975 0.34222412109375 0.078521728515625 -0.7704772949218753 +0.29762500000000004 0.33837890625 0.07763671875 -0.7704772949218753 +0.29775 0.33837890625 0.07763671875 -0.7704772949218753 +0.297875 0.334503173828125 0.076751708984375 -0.7704772949218753 +0.298 0.330596923828125 0.075836181640625 -0.7704772949218753 +0.29812500000000004 0.330596923828125 0.075836181640625 -0.7704772949218753 +0.29825 0.32666015625 0.074951171875 -0.7704772949218753 +0.298375 0.32666015625 0.074951171875 -0.7704772949218753 +0.2985 0.322662353515625 0.07403564453125 -0.7704772949218753 +0.29862500000000004 0.31866455078125 0.073089599609375 -0.7704772949218753 +0.29875 0.31866455078125 0.073089599609375 -0.7704772949218753 +0.298875 0.314605712890625 0.072174072265625 -0.7704772949218753 +0.299 0.314605712890625 0.072174072265625 -0.7704772949218753 +0.29912500000000004 0.310516357421875 0.07122802734375 -0.7704772949218753 +0.29925 0.306396484375 0.070281982421875 -0.7704772949218753 +0.299375 0.306396484375 0.070281982421875 -0.7704772949218753 +0.2995 0.30224609375 0.0693359375 -0.7704772949218753 +0.29962500000000004 0.30224609375 0.0693359375 -0.7704772949218753 +0.29975 0.298065185546875 0.068389892578125 -0.7704772949218753 +0.299875 0.2938232421875 0.067413330078125 -0.7704772949218753 +0.3 0.2938232421875 0.067413330078125 -0.7704772949218753 +0.30012500000000004 0.289581298828125 0.066436767578125 -0.7704772949218753 +0.30025 0.289581298828125 0.066436767578125 -0.7704772949218753 +0.300375 0.285308837890625 0.065460205078125 -0.7704772949218753 +0.3005 0.280975341796875 0.064453125 -0.7704772949218753 +0.30062500000000004 0.280975341796875 0.064453125 -0.7704772949218753 +0.30075 0.276641845703125 0.0634765625 -0.7704772949218753 +0.300875 0.276641845703125 0.0634765625 -0.7704772949218753 +0.301 0.27227783203125 0.062469482421875 -0.7704772949218753 +0.30112500000000004 0.267852783203125 0.06146240234375 -0.7704772949218753 +0.30125 0.267852783203125 0.06146240234375 -0.7704772949218753 +0.301375 0.263427734375 0.0604248046875 -0.7704772949218753 +0.3015 0.263427734375 0.0604248046875 -0.7704772949218753 +0.30162500000000004 0.25897216796875 0.059417724609375 -0.7704772949218753 +0.30175 0.25445556640625 0.058380126953125 -0.7704772949218753 +0.301875 0.25445556640625 0.058380126953125 -0.7704772949218753 +0.302 0.249969482421875 0.057342529296875 -0.7704772949218753 +0.30212500000000004 0.249969482421875 0.057342529296875 -0.7704772949218753 +0.30225 0.24542236328125 0.056304931640625 -0.7704772949218753 +0.302375 0.2408447265625 0.05523681640625 -0.7704772949218753 +0.3025 0.2408447265625 0.05523681640625 -0.7704772949218753 +0.30262500000000004 0.236236572265625 0.05419921875 -0.7704772949218753 +0.30275 0.236236572265625 0.05419921875 -0.7704772949218753 +0.302875 0.231597900390625 0.053131103515625 -0.7704772949218753 +0.303 0.226959228515625 0.05206298828125 -0.7704772949218753 +0.30312500000000004 0.226959228515625 0.05206298828125 -0.7704772949218753 +0.30325 0.2222900390625 0.050994873046875 -0.7704772949218753 +0.303375 0.2222900390625 0.050994873046875 -0.7704772949218753 +0.3035 0.21759033203125 0.049896240234375 -0.7704772949218753 +0.30362500000000004 0.212860107421875 0.048828125 -0.7704772949218753 +0.30375 0.212860107421875 0.048828125 -0.7704772949218753 +0.303875 0.208099365234375 0.0477294921875 -0.7704772949218753 +0.304 0.208099365234375 0.0477294921875 -0.7704772949218753 +0.304125 0.203338623046875 0.046630859375 -0.7704772949218753 +0.30425 0.19854736328125 0.0455322265625 -0.7704772949218753 +0.304375 0.19854736328125 0.0455322265625 -0.7704772949218753 +0.3045 0.1937255859375 0.04443359375 -0.7704772949218753 +0.304625 0.1937255859375 0.04443359375 -0.7704772949218753 +0.30475 0.188873291015625 0.0433349609375 -0.7704772949218753 +0.304875 0.18402099609375 0.042205810546875 -0.7704772949218753 +0.305 0.18402099609375 0.042205810546875 -0.7704772949218753 +0.305125 0.17913818359375 0.04107666015625 -0.7704772949218753 +0.30525 0.17913818359375 0.04107666015625 -0.7704772949218753 +0.305375 0.17425537109375 0.03997802734375 -0.7704772949218753 +0.3055 0.169342041015625 0.038848876953125 -0.7704772949218753 +0.305625 0.169342041015625 0.038848876953125 -0.7704772949218753 +0.30575 0.164398193359375 0.0377197265625 -0.7704772949218753 +0.305875 0.164398193359375 0.0377197265625 -0.7704772949218753 +0.306 0.159454345703125 0.03656005859375 -0.7704772949218753 +0.306125 0.15447998046875 0.035430908203125 -0.7704772949218753 +0.30625 0.15447998046875 0.035430908203125 -0.7704772949218753 +0.306375 0.14947509765625 0.034271240234375 -0.7704772949218753 +0.3065 0.14947509765625 0.034271240234375 -0.7704772949218753 +0.306625 0.14447021484375 0.03314208984375 -0.7704772949218753 +0.30675 0.13946533203125 0.031982421875 -0.7704772949218753 +0.306875 0.13946533203125 0.031982421875 -0.7704772949218753 +0.307 0.134429931640625 0.03082275390625 -0.7704772949218753 +0.307125 0.134429931640625 0.03082275390625 -0.7704772949218753 +0.30725 0.129364013671875 0.0296630859375 -0.7704772949218753 +0.307375 0.124298095703125 0.02850341796875 -0.7704772949218753 +0.3075 0.124298095703125 0.02850341796875 -0.7704772949218753 +0.307625 0.119232177734375 0.02734375 -0.7704772949218753 +0.30775 0.119232177734375 0.02734375 -0.7704772949218753 +0.307875 0.1141357421875 0.02618408203125 -0.7704772949218753 +0.308 0.109039306640625 0.024993896484375 -0.7704772949218753 +0.308125 0.109039306640625 0.024993896484375 -0.7704772949218753 +0.30825 0.103912353515625 0.023834228515625 -0.7704772949218753 +0.308375 0.103912353515625 0.023834228515625 -0.7704772949218753 +0.3085 0.098785400390625 0.02264404296875 -0.7704772949218753 +0.308625 0.093658447265625 0.021484375 -0.7704772949218753 +0.30875 0.093658447265625 0.021484375 -0.7704772949218753 +0.308875 0.0885009765625 0.020294189453125 -0.7704772949218753 +0.309 0.0885009765625 0.020294189453125 -0.7704772949218753 +0.309125 0.083343505859375 0.01910400390625 -0.7704772949218753 +0.30925 0.07818603515625 0.017913818359375 -0.7704772949218753 +0.309375 0.07818603515625 0.017913818359375 -0.7704772949218753 +0.3095 0.072998046875 0.016754150390625 -0.7704772949218753 +0.309625 0.072998046875 0.016754150390625 -0.7704772949218753 +0.30975 0.06781005859375 0.01556396484375 -0.7704772949218753 +0.309875 0.0626220703125 0.014373779296875 -0.7704772949218753 +0.31 0.0626220703125 0.014373779296875 -0.7704772949218753 +0.310125 0.05743408203125 0.01318359375 -0.7704772949218753 +0.31025 0.05743408203125 0.01318359375 -0.7704772949218753 +0.310375 0.05224609375 0.011962890625 -0.7704772949218753 +0.3105 0.047027587890625 0.010772705078125 -0.7704772949218753 +0.310625 0.047027587890625 0.010772705078125 -0.7704772949218753 +0.31075 0.04180908203125 0.00958251953125 -0.7704772949218753 +0.310875 0.04180908203125 0.00958251953125 -0.7704772949218753 +0.311 0.036590576171875 0.008392333984375 -0.7704772949218753 +0.311125 0.0313720703125 0.007171630859375 -0.7704772949218753 +0.31125 0.0313720703125 0.007171630859375 -0.7704772949218753 +0.311375 0.026123046875 0.0059814453125 -0.7704772949218753 +0.3115 0.026123046875 0.0059814453125 -0.7704772949218753 +0.311625 0.020904541015625 0.004791259765625 -0.7704772949218753 +0.31175 0.01568603515625 0.00360107421875 -0.7704772949218753 +0.311875 0.01568603515625 0.00360107421875 -0.7704772949218753 +0.312 0.01043701171875 0.00238037109375 -0.7704772949218753 +0.312125 0.01043701171875 0.00238037109375 -0.7704772949218753 +0.31225 0.005218505859375 0.001190185546875 -0.7704772949218753 +0.312375 0.0 0.0 -0.7704772949218753 +0.3125 0.0 0.0 -0.7704772949218753 +0.312625 -0.005218505859375 -0.001190185546875 -0.7704772949218753 +0.31275000000000004 -0.005218505859375 -0.001190185546875 -0.7704772949218753 +0.312875 -0.01043701171875 -0.00238037109375 -0.7704772949218753 +0.313 -0.01568603515625 -0.00360107421875 -0.7704772949218753 +0.313125 -0.01568603515625 -0.00360107421875 -0.7704772949218753 +0.31325000000000004 -0.020904541015625 -0.004791259765625 -0.7704772949218753 +0.313375 -0.020904541015625 -0.004791259765625 -0.7704772949218753 +0.3135 -0.026123046875 -0.0059814453125 -0.7704772949218753 +0.313625 -0.0313720703125 -0.007171630859375 -0.7704772949218753 +0.31375000000000004 -0.0313720703125 -0.007171630859375 -0.7704772949218753 +0.313875 -0.036590576171875 -0.008392333984375 -0.7704772949218753 +0.314 -0.036590576171875 -0.008392333984375 -0.7704772949218753 +0.314125 -0.04180908203125 -0.00958251953125 -0.7704772949218753 +0.31425000000000004 -0.047027587890625 -0.010772705078125 -0.7704772949218753 +0.314375 -0.047027587890625 -0.010772705078125 -0.7704772949218753 +0.3145 -0.05224609375 -0.011962890625 -0.7704772949218753 +0.314625 -0.05224609375 -0.011962890625 -0.7704772949218753 +0.31475000000000004 -0.05743408203125 -0.01318359375 -0.7704772949218753 +0.314875 -0.0626220703125 -0.014373779296875 -0.7704772949218753 +0.315 -0.0626220703125 -0.014373779296875 -0.7704772949218753 +0.315125 -0.06781005859375 -0.01556396484375 -0.7704772949218753 +0.31525000000000004 -0.06781005859375 -0.01556396484375 -0.7704772949218753 +0.315375 -0.072998046875 -0.016754150390625 -0.7704772949218753 +0.3155 -0.07818603515625 -0.017913818359375 -0.7704772949218753 +0.315625 -0.07818603515625 -0.017913818359375 -0.7704772949218753 +0.31575000000000004 -0.083343505859375 -0.01910400390625 -0.7704772949218753 +0.315875 -0.083343505859375 -0.01910400390625 -0.7704772949218753 +0.316 -0.0885009765625 -0.020294189453125 -0.7704772949218753 +0.316125 -0.093658447265625 -0.021484375 -0.7704772949218753 +0.31625000000000004 -0.093658447265625 -0.021484375 -0.7704772949218753 +0.316375 -0.098785400390625 -0.02264404296875 -0.7704772949218753 +0.3165 -0.098785400390625 -0.02264404296875 -0.7704772949218753 +0.316625 -0.103912353515625 -0.023834228515625 -0.7704772949218753 +0.31675000000000004 -0.109039306640625 -0.024993896484375 -0.7704772949218753 +0.316875 -0.109039306640625 -0.024993896484375 -0.7704772949218753 +0.317 -0.1141357421875 -0.02618408203125 -0.7704772949218753 +0.317125 -0.1141357421875 -0.02618408203125 -0.7704772949218753 +0.31725000000000004 -0.119232177734375 -0.02734375 -0.7704772949218753 +0.317375 -0.124298095703125 -0.02850341796875 -0.7704772949218753 +0.3175 -0.124298095703125 -0.02850341796875 -0.7704772949218753 +0.317625 -0.129364013671875 -0.0296630859375 -0.7704772949218753 +0.31775000000000004 -0.129364013671875 -0.0296630859375 -0.7704772949218753 +0.317875 -0.134429931640625 -0.03082275390625 -0.7704772949218753 +0.318 -0.13946533203125 -0.031982421875 -0.7704772949218753 +0.318125 -0.13946533203125 -0.031982421875 -0.7704772949218753 +0.31825000000000004 -0.14447021484375 -0.03314208984375 -0.7704772949218753 +0.318375 -0.14447021484375 -0.03314208984375 -0.7704772949218753 +0.3185 -0.14947509765625 -0.034271240234375 -0.7704772949218753 +0.318625 -0.15447998046875 -0.035430908203125 -0.7704772949218753 +0.31875000000000004 -0.15447998046875 -0.035430908203125 -0.7704772949218753 +0.318875 -0.159454345703125 -0.03656005859375 -0.7704772949218753 +0.319 -0.159454345703125 -0.03656005859375 -0.7704772949218753 +0.319125 -0.164398193359375 -0.0377197265625 -0.7704772949218753 +0.31925000000000004 -0.169342041015625 -0.038848876953125 -0.7704772949218753 +0.319375 -0.169342041015625 -0.038848876953125 -0.7704772949218753 +0.3195 -0.17425537109375 -0.03997802734375 -0.7704772949218753 +0.319625 -0.17425537109375 -0.03997802734375 -0.7704772949218753 +0.31975000000000004 -0.17913818359375 -0.04107666015625 -0.7704772949218753 +0.319875 -0.18402099609375 -0.042205810546875 -0.7704772949218753 +0.32 -0.18402099609375 -0.007598876953125 -0.9584594726562502912 +0.320125 -0.188873291015625 -0.0078125 -0.9584594726562502912 +0.32025 -0.188873291015625 -0.0078125 -0.9584594726562502912 +0.320375 -0.1937255859375 -0.008026123046875 -0.9584594726562502912 +0.3205 -0.19854736328125 -0.008209228515625 -0.9584594726562502912 +0.320625 -0.19854736328125 -0.008209228515625 -0.9584594726562502912 +0.32075 -0.203338623046875 -0.0084228515625 -0.9584594726562502912 +0.320875 -0.203338623046875 -0.0084228515625 -0.9584594726562502912 +0.321 -0.208099365234375 -0.00860595703125 -0.9584594726562502912 +0.321125 -0.212860107421875 -0.008819580078125 -0.9584594726562502912 +0.32125 -0.212860107421875 -0.008819580078125 -0.9584594726562502912 +0.321375 -0.21759033203125 -0.009002685546875 -0.9584594726562502912 +0.3215 -0.21759033203125 -0.009002685546875 -0.9584594726562502912 +0.321625 -0.2222900390625 -0.009185791015625 -0.9584594726562502912 +0.32175 -0.226959228515625 -0.0093994140625 -0.9584594726562502912 +0.321875 -0.226959228515625 -0.0093994140625 -0.9584594726562502912 +0.322 -0.231597900390625 -0.00958251953125 -0.9584594726562502912 +0.322125 -0.231597900390625 -0.00958251953125 -0.9584594726562502912 +0.32225 -0.236236572265625 -0.009765625 -0.9584594726562502912 +0.322375 -0.2408447265625 -0.009979248046875 -0.9584594726562502912 +0.3225 -0.2408447265625 -0.009979248046875 -0.9584594726562502912 +0.322625 -0.24542236328125 -0.010162353515625 -0.9584594726562502912 +0.32275 -0.24542236328125 -0.010162353515625 -0.9584594726562502912 +0.322875 -0.249969482421875 -0.010345458984375 -0.9584594726562502912 +0.323 -0.25445556640625 -0.010528564453125 -0.9584594726562502912 +0.323125 -0.25445556640625 -0.010528564453125 -0.9584594726562502912 +0.32325 -0.25897216796875 -0.010711669921875 -0.9584594726562502912 +0.323375 -0.25897216796875 -0.010711669921875 -0.9584594726562502912 +0.3235 -0.263427734375 -0.010894775390625 -0.9584594726562502912 +0.323625 -0.267852783203125 -0.011077880859375 -0.9584594726562502912 +0.32375 -0.267852783203125 -0.011077880859375 -0.9584594726562502912 +0.323875 -0.27227783203125 -0.011260986328125 -0.9584594726562502912 +0.324 -0.27227783203125 -0.011260986328125 -0.9584594726562502912 +0.324125 -0.276641845703125 -0.011444091796875 -0.9584594726562502912 +0.32425 -0.280975341796875 -0.011627197265625 -0.9584594726562502912 +0.324375 -0.280975341796875 -0.011627197265625 -0.9584594726562502912 +0.3245 -0.285308837890625 -0.011810302734375 -0.9584594726562502912 +0.324625 -0.285308837890625 -0.011810302734375 -0.9584594726562502912 +0.32475 -0.289581298828125 -0.011993408203125 -0.9584594726562502912 +0.324875 -0.2938232421875 -0.012176513671875 -0.9584594726562502912 +0.325 -0.2938232421875 -0.012176513671875 -0.9584594726562502912 +0.325125 -0.298065185546875 -0.0123291015625 -0.9584594726562502912 +0.32525 -0.298065185546875 -0.0123291015625 -0.9584594726562502912 +0.325375 -0.30224609375 -0.01251220703125 -0.9584594726562502912 +0.3255 -0.306396484375 -0.0126953125 -0.9584594726562502912 +0.325625 -0.306396484375 -0.0126953125 -0.9584594726562502912 +0.32575 -0.310516357421875 -0.012847900390625 -0.9584594726562502912 +0.325875 -0.310516357421875 -0.012847900390625 -0.9584594726562502912 +0.326 -0.314605712890625 -0.013031005859375 -0.9584594726562502912 +0.326125 -0.31866455078125 -0.01318359375 -0.9584594726562502912 +0.32625 -0.31866455078125 -0.01318359375 -0.9584594726562502912 +0.326375 -0.322662353515625 -0.01336669921875 -0.9584594726562502912 +0.3265 -0.322662353515625 -0.01336669921875 -0.9584594726562502912 +0.326625 -0.32666015625 -0.013519287109375 -0.9584594726562502912 +0.32675 -0.330596923828125 -0.013702392578125 -0.9584594726562502912 +0.326875 -0.330596923828125 -0.013702392578125 -0.9584594726562502912 +0.327 -0.334503173828125 -0.01385498046875 -0.9584594726562502912 +0.327125 -0.334503173828125 -0.01385498046875 -0.9584594726562502912 +0.32725 -0.33837890625 -0.014007568359375 -0.9584594726562502912 +0.327375 -0.34222412109375 -0.01416015625 -0.9584594726562502912 +0.3275 -0.34222412109375 -0.01416015625 -0.9584594726562502912 +0.327625 -0.34600830078125 -0.014312744140625 -0.9584594726562502912 +0.32775 -0.34600830078125 -0.014312744140625 -0.9584594726562502912 +0.327875 -0.349761962890625 -0.014495849609375 -0.9584594726562502912 +0.328 -0.353485107421875 -0.0146484375 -0.9584594726562502912 +0.328125 -0.353485107421875 -0.0146484375 -0.9584594726562502912 +0.32825 -0.357177734375 -0.014801025390625 -0.9584594726562502912 +0.32837500000000004 -0.357177734375 -0.014801025390625 -0.9584594726562502912 +0.3285 -0.360809326171875 -0.01495361328125 -0.9584594726562502912 +0.328625 -0.36444091796875 -0.01507568359375 -0.9584594726562502912 +0.32875 -0.36444091796875 -0.01507568359375 -0.9584594726562502912 +0.32887500000000004 -0.36798095703125 -0.015228271484375 -0.9584594726562502912 +0.329 -0.36798095703125 -0.015228271484375 -0.9584594726562502912 +0.329125 -0.37152099609375 -0.015380859375 -0.9584594726562502912 +0.32925 -0.375 -0.015533447265625 -0.9584594726562502912 +0.32937500000000004 -0.375 -0.015533447265625 -0.9584594726562502912 +0.3295 -0.378448486328125 -0.015655517578125 -0.9584594726562502912 +0.329625 -0.378448486328125 -0.015655517578125 -0.9584594726562502912 +0.32975 -0.3818359375 -0.01580810546875 -0.9584594726562502912 +0.32987500000000004 -0.38519287109375 -0.015960693359375 -0.9584594726562502912 +0.33 -0.38519287109375 -0.015960693359375 -0.9584594726562502912 +0.330125 -0.388519287109375 -0.016082763671875 -0.9584594726562502912 +0.33025 -0.388519287109375 -0.016082763671875 -0.9584594726562502912 +0.33037500000000004 -0.39178466796875 -0.0162353515625 -0.9584594726562502912 +0.3305 -0.39501953125 -0.016357421875 -0.9584594726562502912 +0.330625 -0.39501953125 -0.016357421875 -0.9584594726562502912 +0.33075 -0.398193359375 -0.0164794921875 -0.9584594726562502912 +0.33087500000000004 -0.398193359375 -0.0164794921875 -0.9584594726562502912 +0.331 -0.401336669921875 -0.016632080078125 -0.9584594726562502912 +0.331125 -0.404449462890625 -0.016754150390625 -0.9584594726562502912 +0.33125 -0.404449462890625 -0.016754150390625 -0.9584594726562502912 +0.33137500000000004 -0.407501220703125 -0.016876220703125 -0.9584594726562502912 +0.3315 -0.407501220703125 -0.016876220703125 -0.9584594726562502912 +0.331625 -0.4105224609375 -0.016998291015625 -0.9584594726562502912 +0.33175 -0.413482666015625 -0.017120361328125 -0.9584594726562502912 +0.33187500000000004 -0.413482666015625 -0.017120361328125 -0.9584594726562502912 +0.332 -0.416412353515625 -0.017242431640625 -0.9584594726562502912 +0.332125 -0.416412353515625 -0.017242431640625 -0.9584594726562502912 +0.33225 -0.419281005859375 -0.017364501953125 -0.9584594726562502912 +0.33237500000000004 -0.422119140625 -0.017486572265625 -0.9584594726562502912 +0.3325 -0.422119140625 -0.017486572265625 -0.9584594726562502912 +0.332625 -0.424896240234375 -0.017608642578125 -0.9584594726562502912 +0.33275 -0.424896240234375 -0.017608642578125 -0.9584594726562502912 +0.33287500000000004 -0.4276123046875 -0.0177001953125 -0.9584594726562502912 +0.333 -0.4302978515625 -0.017822265625 -0.9584594726562502912 +0.333125 -0.4302978515625 -0.017822265625 -0.9584594726562502912 +0.33325 -0.432952880859375 -0.0179443359375 -0.9584594726562502912 +0.33337500000000004 -0.432952880859375 -0.0179443359375 -0.9584594726562502912 +0.3335 -0.435546875 -0.018035888671875 -0.9584594726562502912 +0.333625 -0.438079833984375 -0.01812744140625 -0.9584594726562502912 +0.33375 -0.438079833984375 -0.01812744140625 -0.9584594726562502912 +0.33387500000000004 -0.440582275390625 -0.01824951171875 -0.9584594726562502912 +0.334 -0.440582275390625 -0.01824951171875 -0.9584594726562502912 +0.334125 -0.44305419921875 -0.018341064453125 -0.9584594726562502912 +0.33425 -0.4454345703125 -0.0184326171875 -0.9584594726562502912 +0.33437500000000004 -0.4454345703125 -0.0184326171875 -0.9584594726562502912 +0.3345 -0.447784423828125 -0.0185546875 -0.9584594726562502912 +0.334625 -0.447784423828125 -0.0185546875 -0.9584594726562502912 +0.33475 -0.450103759765625 -0.018646240234375 -0.9584594726562502912 +0.33487500000000004 -0.452362060546875 -0.01873779296875 -0.9584594726562502912 +0.335 -0.452362060546875 -0.01873779296875 -0.9584594726562502912 +0.335125 -0.454559326171875 -0.018829345703125 -0.9584594726562502912 +0.33525 -0.454559326171875 -0.018829345703125 -0.9584594726562502912 +0.33537500000000004 -0.45672607421875 -0.0189208984375 -0.9584594726562502912 +0.3355 -0.458831787109375 -0.019012451171875 -0.9584594726562502912 +0.335625 -0.458831787109375 -0.019012451171875 -0.9584594726562502912 +0.33575 -0.46087646484375 -0.019073486328125 -0.9584594726562502912 +0.33587500000000004 -0.46087646484375 -0.019073486328125 -0.9584594726562502912 +0.336 -0.462890625 -0.0191650390625 -0.9584594726562502912 +0.336125 -0.464813232421875 -0.019256591796875 -0.9584594726562502912 +0.33625 -0.464813232421875 -0.019256591796875 -0.9584594726562502912 +0.336375 -0.46673583984375 -0.019317626953125 -0.9584594726562502912 +0.3365 -0.46673583984375 -0.019317626953125 -0.9584594726562502912 +0.336625 -0.46856689453125 -0.0194091796875 -0.9584594726562502912 +0.33675 -0.470367431640625 -0.01947021484375 -0.9584594726562502912 +0.336875 -0.470367431640625 -0.01947021484375 -0.9584594726562502912 +0.337 -0.472137451171875 -0.019561767578125 -0.9584594726562502912 +0.337125 -0.472137451171875 -0.019561767578125 -0.9584594726562502912 +0.33725 -0.47381591796875 -0.019622802734375 -0.9584594726562502912 +0.337375 -0.4754638671875 -0.019683837890625 -0.9584594726562502912 +0.3375 -0.4754638671875 -0.019683837890625 -0.9584594726562502912 +0.337625 -0.47705078125 -0.019744873046875 -0.9584594726562502912 +0.33775 -0.47705078125 -0.019744873046875 -0.9584594726562502912 +0.337875 -0.478607177734375 -0.019805908203125 -0.9584594726562502912 +0.338 -0.480072021484375 -0.019866943359375 -0.9584594726562502912 +0.338125 -0.480072021484375 -0.019866943359375 -0.9584594726562502912 +0.33825 -0.48150634765625 -0.019927978515625 -0.9584594726562502912 +0.338375 -0.48150634765625 -0.019927978515625 -0.9584594726562502912 +0.3385 -0.48291015625 -0.019989013671875 -0.9584594726562502912 +0.338625 -0.484222412109375 -0.020050048828125 -0.9584594726562502912 +0.33875 -0.484222412109375 -0.020050048828125 -0.9584594726562502912 +0.338875 -0.485504150390625 -0.020111083984375 -0.9584594726562502912 +0.339 -0.485504150390625 -0.020111083984375 -0.9584594726562502912 +0.339125 -0.486724853515625 -0.020172119140625 -0.9584594726562502912 +0.33925 -0.487884521484375 -0.02020263671875 -0.9584594726562502912 +0.339375 -0.487884521484375 -0.02020263671875 -0.9584594726562502912 +0.3395 -0.489013671875 -0.020263671875 -0.9584594726562502912 +0.339625 -0.489013671875 -0.020263671875 -0.9584594726562502912 +0.33975 -0.490081787109375 -0.020294189453125 -0.9584594726562502912 +0.339875 -0.4910888671875 -0.02032470703125 -0.9584594726562502912 +0.34 -0.4910888671875 -0.02032470703125 -0.9584594726562502912 +0.340125 -0.492034912109375 -0.0203857421875 -0.9584594726562502912 +0.34025 -0.492034912109375 -0.0203857421875 -0.9584594726562502912 +0.340375 -0.492950439453125 -0.020416259765625 -0.9584594726562502912 +0.3405 -0.4937744140625 -0.02044677734375 -0.9584594726562502912 +0.340625 -0.4937744140625 -0.02044677734375 -0.9584594726562502912 +0.34075 -0.49456787109375 -0.020477294921875 -0.9584594726562502912 +0.340875 -0.49456787109375 -0.020477294921875 -0.9584594726562502912 +0.341 -0.49530029296875 -0.0205078125 -0.9584594726562502912 +0.341125 -0.496002197265625 -0.020538330078125 -0.9584594726562502912 +0.34125 -0.496002197265625 -0.020538330078125 -0.9584594726562502912 +0.341375 -0.496612548828125 -0.02056884765625 -0.9584594726562502912 +0.3415 -0.496612548828125 -0.02056884765625 -0.9584594726562502912 +0.341625 -0.4971923828125 -0.020599365234375 -0.9584594726562502912 +0.34175 -0.497711181640625 -0.020599365234375 -0.9584594726562502912 +0.341875 -0.497711181640625 -0.020599365234375 -0.9584594726562502912 +0.342 -0.498199462890625 -0.0206298828125 -0.9584594726562502912 +0.342125 -0.498199462890625 -0.0206298828125 -0.9584594726562502912 +0.34225 -0.49859619140625 -0.020660400390625 -0.9584594726562502912 +0.342375 -0.49896240234375 -0.020660400390625 -0.9584594726562502912 +0.3425 -0.49896240234375 -0.020660400390625 -0.9584594726562502912 +0.342625 -0.499267578125 -0.02069091796875 -0.9584594726562502912 +0.34275 -0.499267578125 -0.02069091796875 -0.9584594726562502912 +0.342875 -0.49951171875 -0.02069091796875 -0.9584594726562502912 +0.343 -0.49969482421875 -0.02069091796875 -0.9584594726562502912 +0.343125 -0.49969482421875 -0.02069091796875 -0.9584594726562502912 +0.34325 -0.49981689453125 -0.02069091796875 -0.9584594726562502912 +0.343375 -0.49981689453125 -0.02069091796875 -0.9584594726562502912 +0.3435 -0.499908447265625 -0.02069091796875 -0.9584594726562502912 +0.343625 -0.49993896484375 -0.02069091796875 -0.9584594726562502912 +0.34375 -0.49993896484375 -0.02069091796875 -0.9584594726562502912 +0.343875 -0.499908447265625 -0.02069091796875 -0.9584594726562502912 +0.34400000000000004 -0.499908447265625 -0.02069091796875 -0.9584594726562502912 +0.344125 -0.49981689453125 -0.02069091796875 -0.9584594726562502912 +0.34425 -0.49969482421875 -0.02069091796875 -0.9584594726562502912 +0.344375 -0.49969482421875 -0.02069091796875 -0.9584594726562502912 +0.34450000000000004 -0.49951171875 -0.02069091796875 -0.9584594726562502912 +0.344625 -0.49951171875 -0.02069091796875 -0.9584594726562502912 +0.34475 -0.499267578125 -0.02069091796875 -0.9584594726562502912 +0.344875 -0.49896240234375 -0.020660400390625 -0.9584594726562502912 +0.34500000000000004 -0.49896240234375 -0.020660400390625 -0.9584594726562502912 +0.345125 -0.49859619140625 -0.020660400390625 -0.9584594726562502912 +0.34525 -0.49859619140625 -0.020660400390625 -0.9584594726562502912 +0.345375 -0.498199462890625 -0.0206298828125 -0.9584594726562502912 +0.34550000000000004 -0.497711181640625 -0.020599365234375 -0.9584594726562502912 +0.345625 -0.497711181640625 -0.020599365234375 -0.9584594726562502912 +0.34575 -0.4971923828125 -0.020599365234375 -0.9584594726562502912 +0.345875 -0.4971923828125 -0.020599365234375 -0.9584594726562502912 +0.34600000000000004 -0.496612548828125 -0.02056884765625 -0.9584594726562502912 +0.346125 -0.496002197265625 -0.020538330078125 -0.9584594726562502912 +0.34625 -0.496002197265625 -0.020538330078125 -0.9584594726562502912 +0.346375 -0.49530029296875 -0.0205078125 -0.9584594726562502912 +0.34650000000000004 -0.49530029296875 -0.0205078125 -0.9584594726562502912 +0.346625 -0.49456787109375 -0.020477294921875 -0.9584594726562502912 +0.34675 -0.4937744140625 -0.02044677734375 -0.9584594726562502912 +0.346875 -0.4937744140625 -0.02044677734375 -0.9584594726562502912 +0.34700000000000004 -0.492950439453125 -0.020416259765625 -0.9584594726562502912 +0.347125 -0.492950439453125 -0.020416259765625 -0.9584594726562502912 +0.34725 -0.492034912109375 -0.0203857421875 -0.9584594726562502912 +0.347375 -0.4910888671875 -0.02032470703125 -0.9584594726562502912 +0.34750000000000004 -0.4910888671875 -0.02032470703125 -0.9584594726562502912 +0.347625 -0.490081787109375 -0.020294189453125 -0.9584594726562502912 +0.34775 -0.490081787109375 -0.020294189453125 -0.9584594726562502912 +0.347875 -0.489013671875 -0.020263671875 -0.9584594726562502912 +0.34800000000000004 -0.487884521484375 -0.02020263671875 -0.9584594726562502912 +0.348125 -0.487884521484375 -0.02020263671875 -0.9584594726562502912 +0.34825 -0.486724853515625 -0.020172119140625 -0.9584594726562502912 +0.348375 -0.486724853515625 -0.020172119140625 -0.9584594726562502912 +0.34850000000000004 -0.485504150390625 -0.020111083984375 -0.9584594726562502912 +0.348625 -0.484222412109375 -0.020050048828125 -0.9584594726562502912 +0.34875 -0.484222412109375 -0.020050048828125 -0.9584594726562502912 +0.348875 -0.48291015625 -0.019989013671875 -0.9584594726562502912 +0.34900000000000004 -0.48291015625 -0.019989013671875 -0.9584594726562502912 +0.349125 -0.48150634765625 -0.019927978515625 -0.9584594726562502912 +0.34925 -0.480072021484375 -0.019866943359375 -0.9584594726562502912 +0.349375 -0.480072021484375 -0.019866943359375 -0.9584594726562502912 +0.34950000000000004 -0.478607177734375 -0.019805908203125 -0.9584594726562502912 +0.349625 -0.478607177734375 -0.019805908203125 -0.9584594726562502912 +0.34975 -0.47705078125 -0.019744873046875 -0.9584594726562502912 +0.349875 -0.4754638671875 -0.019683837890625 -0.9584594726562502912 +0.35000000000000004 -0.4754638671875 -0.019683837890625 -0.9584594726562502912 +0.350125 -0.47381591796875 -0.019622802734375 -0.9584594726562502912 +0.35025 -0.47381591796875 -0.019622802734375 -0.9584594726562502912 +0.350375 -0.472137451171875 -0.019561767578125 -0.9584594726562502912 +0.35050000000000004 -0.470367431640625 -0.01947021484375 -0.9584594726562502912 +0.350625 -0.470367431640625 -0.01947021484375 -0.9584594726562502912 +0.35075 -0.46856689453125 -0.0194091796875 -0.9584594726562502912 +0.350875 -0.46856689453125 -0.0194091796875 -0.9584594726562502912 +0.35100000000000004 -0.46673583984375 -0.019317626953125 -0.9584594726562502912 +0.351125 -0.464813232421875 -0.019256591796875 -0.9584594726562502912 +0.35125 -0.464813232421875 -0.019256591796875 -0.9584594726562502912 +0.351375 -0.462890625 -0.0191650390625 -0.9584594726562502912 +0.35150000000000004 -0.462890625 -0.0191650390625 -0.9584594726562502912 +0.351625 -0.46087646484375 -0.019073486328125 -0.9584594726562502912 +0.35175 -0.458831787109375 -0.019012451171875 -0.9584594726562502912 +0.351875 -0.458831787109375 -0.019012451171875 -0.9584594726562502912 +0.352 -0.45672607421875 -0.002899169921875 -0.9935485839843748 +0.352125 -0.45672607421875 -0.002899169921875 -0.9935485839843748 +0.35225 -0.454559326171875 -0.00286865234375 -0.9935485839843748 +0.352375 -0.452362060546875 -0.00286865234375 -0.9935485839843748 +0.3525 -0.452362060546875 -0.00286865234375 -0.9935485839843748 +0.352625 -0.450103759765625 -0.002838134765625 -0.9935485839843748 +0.35275 -0.450103759765625 -0.002838134765625 -0.9935485839843748 +0.352875 -0.447784423828125 -0.002838134765625 -0.9935485839843748 +0.353 -0.4454345703125 -0.0028076171875 -0.9935485839843748 +0.353125 -0.4454345703125 -0.0028076171875 -0.9935485839843748 +0.35325 -0.44305419921875 -0.0028076171875 -0.9935485839843748 +0.353375 -0.44305419921875 -0.0028076171875 -0.9935485839843748 +0.3535 -0.440582275390625 -0.002777099609375 -0.9935485839843748 +0.353625 -0.438079833984375 -0.002777099609375 -0.9935485839843748 +0.35375 -0.438079833984375 -0.002777099609375 -0.9935485839843748 +0.353875 -0.435546875 -0.00274658203125 -0.9935485839843748 +0.354 -0.435546875 -0.00274658203125 -0.9935485839843748 +0.354125 -0.432952880859375 -0.00274658203125 -0.9935485839843748 +0.35425 -0.4302978515625 -0.002716064453125 -0.9935485839843748 +0.354375 -0.4302978515625 -0.002716064453125 -0.9935485839843748 +0.3545 -0.4276123046875 -0.002685546875 -0.9935485839843748 +0.354625 -0.4276123046875 -0.002685546875 -0.9935485839843748 +0.35475 -0.424896240234375 -0.002685546875 -0.9935485839843748 +0.354875 -0.422119140625 -0.002655029296875 -0.9935485839843748 +0.355 -0.422119140625 -0.002655029296875 -0.9935485839843748 +0.355125 -0.419281005859375 -0.002655029296875 -0.9935485839843748 +0.35525 -0.419281005859375 -0.002655029296875 -0.9935485839843748 +0.355375 -0.416412353515625 -0.00262451171875 -0.9935485839843748 +0.3555 -0.413482666015625 -0.00262451171875 -0.9935485839843748 +0.355625 -0.413482666015625 -0.00262451171875 -0.9935485839843748 +0.35575 -0.4105224609375 -0.002593994140625 -0.9935485839843748 +0.355875 -0.4105224609375 -0.002593994140625 -0.9935485839843748 +0.356 -0.407501220703125 -0.0025634765625 -0.9935485839843748 +0.356125 -0.404449462890625 -0.0025634765625 -0.9935485839843748 +0.35625 -0.404449462890625 -0.0025634765625 -0.9935485839843748 +0.356375 -0.401336669921875 -0.002532958984375 -0.9935485839843748 +0.3565 -0.401336669921875 -0.002532958984375 -0.9935485839843748 +0.356625 -0.398193359375 -0.00250244140625 -0.9935485839843748 +0.35675 -0.39501953125 -0.00250244140625 -0.9935485839843748 +0.356875 -0.39501953125 -0.00250244140625 -0.9935485839843748 +0.357 -0.39178466796875 -0.002471923828125 -0.9935485839843748 +0.357125 -0.39178466796875 -0.002471923828125 -0.9935485839843748 +0.35725 -0.388519287109375 -0.00244140625 -0.9935485839843748 +0.357375 -0.38519287109375 -0.00244140625 -0.9935485839843748 +0.3575 -0.38519287109375 -0.00244140625 -0.9935485839843748 +0.357625 -0.3818359375 -0.002410888671875 -0.9935485839843748 +0.35775 -0.3818359375 -0.002410888671875 -0.9935485839843748 +0.357875 -0.378448486328125 -0.00238037109375 -0.9935485839843748 +0.358 -0.375 -0.00238037109375 -0.9935485839843748 +0.358125 -0.375 -0.00238037109375 -0.9935485839843748 +0.35825 -0.37152099609375 -0.002349853515625 -0.9935485839843748 +0.358375 -0.37152099609375 -0.002349853515625 -0.9935485839843748 +0.3585 -0.36798095703125 -0.0023193359375 -0.9935485839843748 +0.358625 -0.36444091796875 -0.002288818359375 -0.9935485839843748 +0.35875 -0.36444091796875 -0.002288818359375 -0.9935485839843748 +0.358875 -0.360809326171875 -0.002288818359375 -0.9935485839843748 +0.359 -0.360809326171875 -0.002288818359375 -0.9935485839843748 +0.359125 -0.357177734375 -0.00225830078125 -0.9935485839843748 +0.35925 -0.353485107421875 -0.002227783203125 -0.9935485839843748 +0.359375 -0.353485107421875 -0.002227783203125 -0.9935485839843748 +0.3595 -0.349761962890625 -0.002197265625 -0.9935485839843748 +0.35962500000000004 -0.349761962890625 -0.002197265625 -0.9935485839843748 +0.35975 -0.34600830078125 -0.002166748046875 -0.9935485839843748 +0.359875 -0.34222412109375 -0.002166748046875 -0.9935485839843748 +0.36 -0.34222412109375 -0.002166748046875 -0.9935485839843748 +0.36012500000000004 -0.33837890625 -0.00213623046875 -0.9935485839843748 +0.36025 -0.33837890625 -0.00213623046875 -0.9935485839843748 +0.360375 -0.334503173828125 -0.002105712890625 -0.9935485839843748 +0.3605 -0.330596923828125 -0.0020751953125 -0.9935485839843748 +0.3606250000000000512 -0.330596923828125 -0.0020751953125 -0.9935485839843748 +0.36075 -0.32666015625 -0.002044677734375 -0.9935485839843748 +0.360875 -0.32666015625 -0.002044677734375 -0.9935485839843748 +0.361 -0.322662353515625 -0.002044677734375 -0.9935485839843748 +0.3611250000000000512 -0.31866455078125 -0.00201416015625 -0.9935485839843748 +0.36125 -0.31866455078125 -0.00201416015625 -0.9935485839843748 +0.361375 -0.314605712890625 -0.001983642578125 -0.9935485839843748 +0.3615 -0.314605712890625 -0.001983642578125 -0.9935485839843748 +0.3616250000000000512 -0.310516357421875 -0.001953125 -0.9935485839843748 +0.36175 -0.306396484375 -0.001922607421875 -0.9935485839843748 +0.361875 -0.306396484375 -0.001922607421875 -0.9935485839843748 +0.362 -0.30224609375 -0.00189208984375 -0.9935485839843748 +0.3621250000000000512 -0.30224609375 -0.00189208984375 -0.9935485839843748 +0.36225 -0.298065185546875 -0.00189208984375 -0.9935485839843748 +0.362375 -0.2938232421875 -0.001861572265625 -0.9935485839843748 +0.3625 -0.2938232421875 -0.001861572265625 -0.9935485839843748 +0.3626250000000000512 -0.289581298828125 -0.0018310546875 -0.9935485839843748 +0.36275 -0.289581298828125 -0.0018310546875 -0.9935485839843748 +0.362875 -0.285308837890625 -0.001800537109375 -0.9935485839843748 +0.363 -0.280975341796875 -0.00177001953125 -0.9935485839843748 +0.3631250000000000512 -0.280975341796875 -0.00177001953125 -0.9935485839843748 +0.36325 -0.276641845703125 -0.001739501953125 -0.9935485839843748 +0.363375 -0.276641845703125 -0.001739501953125 -0.9935485839843748 +0.3635 -0.27227783203125 -0.001708984375 -0.9935485839843748 +0.3636250000000000512 -0.267852783203125 -0.001678466796875 -0.9935485839843748 +0.36375 -0.267852783203125 -0.001678466796875 -0.9935485839843748 +0.363875 -0.263427734375 -0.00164794921875 -0.9935485839843748 +0.364 -0.263427734375 -0.00164794921875 -0.9935485839843748 +0.3641250000000000512 -0.25897216796875 -0.001617431640625 -0.9935485839843748 +0.36425 -0.25445556640625 -0.0015869140625 -0.9935485839843748 +0.364375 -0.25445556640625 -0.0015869140625 -0.9935485839843748 +0.3645 -0.249969482421875 -0.001556396484375 -0.9935485839843748 +0.3646250000000000512 -0.249969482421875 -0.001556396484375 -0.9935485839843748 +0.36475 -0.24542236328125 -0.001556396484375 -0.9935485839843748 +0.364875 -0.2408447265625 -0.00152587890625 -0.9935485839843748 +0.365 -0.2408447265625 -0.00152587890625 -0.9935485839843748 +0.3651250000000000512 -0.236236572265625 -0.001495361328125 -0.9935485839843748 +0.36525 -0.236236572265625 -0.001495361328125 -0.9935485839843748 +0.365375 -0.231597900390625 -0.00146484375 -0.9935485839843748 +0.3655 -0.226959228515625 -0.001434326171875 -0.9935485839843748 +0.3656250000000000512 -0.226959228515625 -0.001434326171875 -0.9935485839843748 +0.36575 -0.2222900390625 -0.00140380859375 -0.9935485839843748 +0.365875 -0.2222900390625 -0.00140380859375 -0.9935485839843748 +0.366 -0.21759033203125 -0.001373291015625 -0.9935485839843748 +0.3661250000000000512 -0.212860107421875 -0.0013427734375 -0.9935485839843748 +0.36625 -0.212860107421875 -0.0013427734375 -0.9935485839843748 +0.366375 -0.208099365234375 -0.001312255859375 -0.9935485839843748 +0.3665 -0.208099365234375 -0.001312255859375 -0.9935485839843748 +0.3666250000000000512 -0.203338623046875 -0.00128173828125 -0.9935485839843748 +0.36675 -0.19854736328125 -0.001251220703125 -0.9935485839843748 +0.366875 -0.19854736328125 -0.001251220703125 -0.9935485839843748 +0.367 -0.1937255859375 -0.001220703125 -0.9935485839843748 +0.3671250000000000512 -0.1937255859375 -0.001220703125 -0.9935485839843748 +0.36725 -0.188873291015625 -0.001190185546875 -0.9935485839843748 +0.367375 -0.18402099609375 -0.00115966796875 -0.9935485839843748 +0.3675 -0.18402099609375 -0.00115966796875 -0.9935485839843748 +0.3676250000000000512 -0.17913818359375 -0.001129150390625 -0.9935485839843748 +0.36775 -0.17913818359375 -0.001129150390625 -0.9935485839843748 +0.367875 -0.17425537109375 -0.0010986328125 -0.9935485839843748 +0.368 -0.169342041015625 -0.001068115234375 -0.9935485839843748 +0.368125 -0.169342041015625 -0.001068115234375 -0.9935485839843748 +0.36825 -0.164398193359375 -0.00103759765625 -0.9935485839843748 +0.368375 -0.164398193359375 -0.00103759765625 -0.9935485839843748 +0.3685 -0.159454345703125 -0.001007080078125 -0.9935485839843748 +0.368625 -0.15447998046875 -0.0009765625 -0.9935485839843748 +0.36875 -0.15447998046875 -0.0009765625 -0.9935485839843748 +0.368875 -0.14947509765625 -0.000946044921875 -0.9935485839843748 +0.369 -0.14947509765625 -0.000946044921875 -0.9935485839843748 +0.369125 -0.14447021484375 -0.00091552734375 -0.9935485839843748 +0.36925 -0.13946533203125 -0.000885009765625 -0.9935485839843748 +0.369375 -0.13946533203125 -0.000885009765625 -0.9935485839843748 +0.3695 -0.134429931640625 -0.000823974609375 -0.9935485839843748 +0.369625 -0.134429931640625 -0.000823974609375 -0.9935485839843748 +0.36975 -0.129364013671875 -0.00079345703125 -0.9935485839843748 +0.369875 -0.124298095703125 -0.000762939453125 -0.9935485839843748 +0.37 -0.124298095703125 -0.000762939453125 -0.9935485839843748 +0.370125 -0.119232177734375 -0.000732421875 -0.9935485839843748 +0.37025 -0.119232177734375 -0.000732421875 -0.9935485839843748 +0.370375 -0.1141357421875 -0.000701904296875 -0.9935485839843748 +0.3705 -0.109039306640625 -0.00067138671875 -0.9935485839843748 +0.370625 -0.109039306640625 -0.00067138671875 -0.9935485839843748 +0.37075 -0.103912353515625 -0.000640869140625 -0.9935485839843748 +0.370875 -0.103912353515625 -0.000640869140625 -0.9935485839843748 +0.371 -0.098785400390625 -0.0006103515625 -0.9935485839843748 +0.371125 -0.093658447265625 -0.000579833984375 -0.9935485839843748 +0.37125 -0.093658447265625 -0.000579833984375 -0.9935485839843748 +0.371375 -0.0885009765625 -0.00054931640625 -0.9935485839843748 +0.3715 -0.0885009765625 -0.00054931640625 -0.9935485839843748 +0.371625 -0.083343505859375 -0.000518798828125 -0.9935485839843748 +0.37175 -0.07818603515625 -0.00048828125 -0.9935485839843748 +0.371875 -0.07818603515625 -0.00048828125 -0.9935485839843748 +0.372 -0.072998046875 -0.000457763671875 -0.9935485839843748 +0.372125 -0.072998046875 -0.000457763671875 -0.9935485839843748 +0.37225 -0.06781005859375 -0.00042724609375 -0.9935485839843748 +0.372375 -0.0626220703125 -0.000396728515625 -0.9935485839843748 +0.3725 -0.0626220703125 -0.000396728515625 -0.9935485839843748 +0.372625 -0.05743408203125 -0.000335693359375 -0.9935485839843748 +0.37275 -0.05743408203125 -0.000335693359375 -0.9935485839843748 +0.372875 -0.05224609375 -0.00030517578125 -0.9935485839843748 +0.373 -0.047027587890625 -0.000274658203125 -0.9935485839843748 +0.373125 -0.047027587890625 -0.000274658203125 -0.9935485839843748 +0.37325 -0.04180908203125 -0.000244140625 -0.9935485839843748 +0.373375 -0.04180908203125 -0.000244140625 -0.9935485839843748 +0.3735 -0.036590576171875 -0.000213623046875 -0.9935485839843748 +0.373625 -0.0313720703125 -0.00018310546875 -0.9935485839843748 +0.37375 -0.0313720703125 -0.00018310546875 -0.9935485839843748 +0.373875 -0.026123046875 -0.000152587890625 -0.9935485839843748 +0.374 -0.026123046875 -0.000152587890625 -0.9935485839843748 +0.374125 -0.020904541015625 -0.0001220703125 -0.9935485839843748 +0.37425 -0.01568603515625 -9.1552734375e-05 -0.9935485839843748 +0.374375 -0.01568603515625 -9.1552734375e-05 -0.9935485839843748 +0.3745 -0.01043701171875 -6.103515625e-05 -0.9935485839843748 +0.374625 -0.01043701171875 -6.103515625e-05 -0.9935485839843748 +0.37475 -0.005218505859375 -3.0517578125e-05 -0.9935485839843748 +0.374875 0.0 0.0 -0.9935485839843748 +0.375 0.0 0.0 -0.9935485839843748 +0.375125 0.005218505859375 3.0517578125e-05 -0.9935485839843748 +0.3752500000000000512 0.005218505859375 3.0517578125e-05 -0.9935485839843748 +0.375375 0.01043701171875 6.103515625e-05 -0.9935485839843748 +0.3755 0.01568603515625 9.1552734375e-05 -0.9935485839843748 +0.375625 0.01568603515625 9.1552734375e-05 -0.9935485839843748 +0.3757500000000000512 0.020904541015625 0.0001220703125 -0.9935485839843748 +0.375875 0.020904541015625 0.0001220703125 -0.9935485839843748 +0.376 0.026123046875 0.000152587890625 -0.9935485839843748 +0.376125 0.0313720703125 0.00018310546875 -0.9935485839843748 +0.3762500000000000512 0.0313720703125 0.00018310546875 -0.9935485839843748 +0.376375 0.036590576171875 0.000213623046875 -0.9935485839843748 +0.3765 0.036590576171875 0.000213623046875 -0.9935485839843748 +0.376625 0.04180908203125 0.000244140625 -0.9935485839843748 +0.3767500000000000512 0.047027587890625 0.000274658203125 -0.9935485839843748 +0.376875 0.047027587890625 0.000274658203125 -0.9935485839843748 +0.377 0.05224609375 0.00030517578125 -0.9935485839843748 +0.377125 0.05224609375 0.00030517578125 -0.9935485839843748 +0.3772500000000000512 0.05743408203125 0.000335693359375 -0.9935485839843748 +0.377375 0.0626220703125 0.000396728515625 -0.9935485839843748 +0.3775 0.0626220703125 0.000396728515625 -0.9935485839843748 +0.377625 0.06781005859375 0.00042724609375 -0.9935485839843748 +0.3777500000000000512 0.06781005859375 0.00042724609375 -0.9935485839843748 +0.377875 0.072998046875 0.000457763671875 -0.9935485839843748 +0.378 0.07818603515625 0.00048828125 -0.9935485839843748 +0.378125 0.07818603515625 0.00048828125 -0.9935485839843748 +0.3782500000000000512 0.083343505859375 0.000518798828125 -0.9935485839843748 +0.378375 0.083343505859375 0.000518798828125 -0.9935485839843748 +0.3785 0.0885009765625 0.00054931640625 -0.9935485839843748 +0.378625 0.093658447265625 0.000579833984375 -0.9935485839843748 +0.3787500000000000512 0.093658447265625 0.000579833984375 -0.9935485839843748 +0.378875 0.098785400390625 0.0006103515625 -0.9935485839843748 +0.379 0.098785400390625 0.0006103515625 -0.9935485839843748 +0.379125 0.103912353515625 0.000640869140625 -0.9935485839843748 +0.3792500000000000512 0.109039306640625 0.00067138671875 -0.9935485839843748 +0.379375 0.109039306640625 0.00067138671875 -0.9935485839843748 +0.3795 0.1141357421875 0.000701904296875 -0.9935485839843748 +0.379625 0.1141357421875 0.000701904296875 -0.9935485839843748 +0.3797500000000000512 0.119232177734375 0.000732421875 -0.9935485839843748 +0.379875 0.124298095703125 0.000762939453125 -0.9935485839843748 +0.38 0.124298095703125 0.000762939453125 -0.9935485839843748 +0.380125 0.129364013671875 0.00079345703125 -0.9935485839843748 +0.3802500000000000512 0.129364013671875 0.00079345703125 -0.9935485839843748 +0.380375 0.134429931640625 0.000823974609375 -0.9935485839843748 +0.3805 0.13946533203125 0.000885009765625 -0.9935485839843748 +0.380625 0.13946533203125 0.000885009765625 -0.9935485839843748 +0.3807500000000000512 0.14447021484375 0.00091552734375 -0.9935485839843748 +0.380875 0.14447021484375 0.00091552734375 -0.9935485839843748 +0.381 0.14947509765625 0.000946044921875 -0.9935485839843748 +0.381125 0.15447998046875 0.0009765625 -0.9935485839843748 +0.3812500000000000512 0.15447998046875 0.0009765625 -0.9935485839843748 +0.381375 0.159454345703125 0.001007080078125 -0.9935485839843748 +0.3815 0.159454345703125 0.001007080078125 -0.9935485839843748 +0.381625 0.164398193359375 0.00103759765625 -0.9935485839843748 +0.3817500000000000512 0.169342041015625 0.001068115234375 -0.9935485839843748 +0.381875 0.169342041015625 0.001068115234375 -0.9935485839843748 +0.382 0.17425537109375 0.0010986328125 -0.9935485839843748 +0.382125 0.17425537109375 0.0010986328125 -0.9935485839843748 +0.3822500000000000512 0.17913818359375 0.001129150390625 -0.9935485839843748 +0.382375 0.18402099609375 0.00115966796875 -0.9935485839843748 +0.3825 0.18402099609375 0.00115966796875 -0.9935485839843748 +0.382625 0.188873291015625 0.001190185546875 -0.9935485839843748 +0.3827500000000000512 0.188873291015625 0.001190185546875 -0.9935485839843748 +0.382875 0.1937255859375 0.001220703125 -0.9935485839843748 +0.383 0.19854736328125 0.001251220703125 -0.9935485839843748 +0.383125 0.19854736328125 0.001251220703125 -0.9935485839843748 +0.3832500000000000512 0.203338623046875 0.00128173828125 -0.9935485839843748 +0.383375 0.203338623046875 0.00128173828125 -0.9935485839843748 +0.3835 0.208099365234375 0.001312255859375 -0.9935485839843748 +0.383625 0.212860107421875 0.0013427734375 -0.9935485839843748 +0.3837500000000000512 0.212860107421875 0.0013427734375 -0.9935485839843748 +0.383875 0.21759033203125 0.001373291015625 -0.9935485839843748 +0.384 0.21759033203125 0.028228759765625 -0.8701477050781241 +0.384125 0.2222900390625 0.028839111328125 -0.8701477050781241 +0.38425 0.226959228515625 0.029449462890625 -0.8701477050781241 +0.384375 0.226959228515625 0.029449462890625 -0.8701477050781241 +0.3845 0.231597900390625 0.030029296875 -0.8701477050781241 +0.384625 0.231597900390625 0.030029296875 -0.8701477050781241 +0.38475 0.236236572265625 0.0306396484375 -0.8701477050781241 +0.384875 0.2408447265625 0.03125 -0.8701477050781241 +0.385 0.2408447265625 0.03125 -0.8701477050781241 +0.385125 0.24542236328125 0.031829833984375 -0.8701477050781241 +0.38525 0.24542236328125 0.031829833984375 -0.8701477050781241 +0.385375 0.249969482421875 0.03240966796875 -0.8701477050781241 +0.3855 0.25445556640625 0.03302001953125 -0.8701477050781241 +0.385625 0.25445556640625 0.03302001953125 -0.8701477050781241 +0.38575 0.25897216796875 0.033599853515625 -0.8701477050781241 +0.385875 0.25897216796875 0.033599853515625 -0.8701477050781241 +0.386 0.263427734375 0.0341796875 -0.8701477050781241 +0.386125 0.267852783203125 0.034759521484375 -0.8701477050781241 +0.38625 0.267852783203125 0.034759521484375 -0.8701477050781241 +0.386375 0.27227783203125 0.035308837890625 -0.8701477050781241 +0.3865 0.27227783203125 0.035308837890625 -0.8701477050781241 +0.386625 0.276641845703125 0.035888671875 -0.8701477050781241 +0.38675 0.280975341796875 0.03643798828125 -0.8701477050781241 +0.386875 0.280975341796875 0.03643798828125 -0.8701477050781241 +0.387 0.285308837890625 0.037017822265625 -0.8701477050781241 +0.387125 0.285308837890625 0.037017822265625 -0.8701477050781241 +0.38725 0.289581298828125 0.037567138671875 -0.8701477050781241 +0.387375 0.2938232421875 0.038116455078125 -0.8701477050781241 +0.3875 0.2938232421875 0.038116455078125 -0.8701477050781241 +0.387625 0.298065185546875 0.038665771484375 -0.8701477050781241 +0.38775 0.298065185546875 0.038665771484375 -0.8701477050781241 +0.387875 0.30224609375 0.039215087890625 -0.8701477050781241 +0.388 0.306396484375 0.03973388671875 -0.8701477050781241 +0.388125 0.306396484375 0.03973388671875 -0.8701477050781241 +0.38825 0.310516357421875 0.040283203125 -0.8701477050781241 +0.388375 0.310516357421875 0.040283203125 -0.8701477050781241 +0.3885 0.314605712890625 0.040802001953125 -0.8701477050781241 +0.388625 0.31866455078125 0.041351318359375 -0.8701477050781241 +0.38875 0.31866455078125 0.041351318359375 -0.8701477050781241 +0.388875 0.322662353515625 0.0418701171875 -0.8701477050781241 +0.389 0.322662353515625 0.0418701171875 -0.8701477050781241 +0.389125 0.32666015625 0.042388916015625 -0.8701477050781241 +0.38925 0.330596923828125 0.042877197265625 -0.8701477050781241 +0.389375 0.330596923828125 0.042877197265625 -0.8701477050781241 +0.3895 0.334503173828125 0.04339599609375 -0.8701477050781241 +0.389625 0.334503173828125 0.04339599609375 -0.8701477050781241 +0.38975 0.33837890625 0.04388427734375 -0.8701477050781241 +0.389875 0.34222412109375 0.044403076171875 -0.8701477050781241 +0.39 0.34222412109375 0.044403076171875 -0.8701477050781241 +0.390125 0.34600830078125 0.044891357421875 -0.8701477050781241 +0.39025 0.34600830078125 0.044891357421875 -0.8701477050781241 +0.390375 0.349761962890625 0.045379638671875 -0.8701477050781241 +0.3905 0.353485107421875 0.045867919921875 -0.8701477050781241 +0.390625 0.353485107421875 0.045867919921875 -0.8701477050781241 +0.39075 0.357177734375 0.04632568359375 -0.8701477050781241 +0.3908750000000000512 0.357177734375 0.04632568359375 -0.8701477050781241 +0.391 0.360809326171875 0.04681396484375 -0.8701477050781241 +0.391125 0.36444091796875 0.047271728515625 -0.8701477050781241 +0.39125 0.36444091796875 0.047271728515625 -0.8701477050781241 +0.3913750000000000512 0.36798095703125 0.0477294921875 -0.8701477050781241 +0.3915 0.36798095703125 0.0477294921875 -0.8701477050781241 +0.391625 0.37152099609375 0.048187255859375 -0.8701477050781241 +0.39175 0.375 0.04864501953125 -0.8701477050781241 +0.3918750000000000512 0.375 0.04864501953125 -0.8701477050781241 +0.392 0.378448486328125 0.049102783203125 -0.8701477050781241 +0.392125 0.378448486328125 0.049102783203125 -0.8701477050781241 +0.39225 0.3818359375 0.049530029296875 -0.8701477050781241 +0.3923750000000000512 0.38519287109375 0.04998779296875 -0.8701477050781241 +0.3925 0.38519287109375 0.04998779296875 -0.8701477050781241 +0.392625 0.388519287109375 0.0504150390625 -0.8701477050781241 +0.39275 0.388519287109375 0.0504150390625 -0.8701477050781241 +0.3928750000000000512 0.39178466796875 0.05084228515625 -0.8701477050781241 +0.393 0.39501953125 0.051239013671875 -0.8701477050781241 +0.393125 0.39501953125 0.051239013671875 -0.8701477050781241 +0.39325 0.398193359375 0.051666259765625 -0.8701477050781241 +0.3933750000000000512 0.398193359375 0.051666259765625 -0.8701477050781241 +0.3935 0.401336669921875 0.05206298828125 -0.8701477050781241 +0.393625 0.404449462890625 0.052459716796875 -0.8701477050781241 +0.39375 0.404449462890625 0.052459716796875 -0.8701477050781241 +0.3938750000000000512 0.407501220703125 0.0528564453125 -0.8701477050781241 +0.394 0.407501220703125 0.0528564453125 -0.8701477050781241 +0.394125 0.4105224609375 0.053253173828125 -0.8701477050781241 +0.39425 0.413482666015625 0.05364990234375 -0.8701477050781241 +0.3943750000000000512 0.413482666015625 0.05364990234375 -0.8701477050781241 +0.3945 0.416412353515625 0.05401611328125 -0.8701477050781241 +0.394625 0.416412353515625 0.05401611328125 -0.8701477050781241 +0.39475 0.419281005859375 0.05438232421875 -0.8701477050781241 +0.3948750000000000512 0.422119140625 0.05474853515625 -0.8701477050781241 +0.395 0.422119140625 0.05474853515625 -0.8701477050781241 +0.395125 0.424896240234375 0.05511474609375 -0.8701477050781241 +0.39525 0.424896240234375 0.05511474609375 -0.8701477050781241 +0.3953750000000000512 0.4276123046875 0.05548095703125 -0.8701477050781241 +0.3955 0.4302978515625 0.055816650390625 -0.8701477050781241 +0.395625 0.4302978515625 0.055816650390625 -0.8701477050781241 +0.39575 0.432952880859375 0.056182861328125 -0.8701477050781241 +0.3958750000000000512 0.432952880859375 0.056182861328125 -0.8701477050781241 +0.396 0.435546875 0.0565185546875 -0.8701477050781241 +0.396125 0.438079833984375 0.05682373046875 -0.8701477050781241 +0.39625 0.438079833984375 0.05682373046875 -0.8701477050781241 +0.3963750000000000512 0.440582275390625 0.057159423828125 -0.8701477050781241 +0.3965 0.440582275390625 0.057159423828125 -0.8701477050781241 +0.396625 0.44305419921875 0.057464599609375 -0.8701477050781241 +0.39675 0.4454345703125 0.05780029296875 -0.8701477050781241 +0.3968750000000000512 0.4454345703125 0.05780029296875 -0.8701477050781241 +0.397 0.447784423828125 0.05810546875 -0.8701477050781241 +0.397125 0.447784423828125 0.05810546875 -0.8701477050781241 +0.39725 0.450103759765625 0.058380126953125 -0.8701477050781241 +0.3973750000000000512 0.452362060546875 0.058685302734375 -0.8701477050781241 +0.3975 0.452362060546875 0.058685302734375 -0.8701477050781241 +0.397625 0.454559326171875 0.0589599609375 -0.8701477050781241 +0.39775 0.454559326171875 0.0589599609375 -0.8701477050781241 +0.3978750000000000512 0.45672607421875 0.05926513671875 -0.8701477050781241 +0.398 0.458831787109375 0.059539794921875 -0.8701477050781241 +0.398125 0.458831787109375 0.059539794921875 -0.8701477050781241 +0.39825 0.46087646484375 0.059783935546875 -0.8701477050781241 +0.3983750000000000512 0.46087646484375 0.059783935546875 -0.8701477050781241 +0.3985 0.462890625 0.06005859375 -0.8701477050781241 +0.398625 0.464813232421875 0.060302734375 -0.8701477050781241 +0.39875 0.464813232421875 0.060302734375 -0.8701477050781241 +0.3988750000000000512 0.46673583984375 0.060546875 -0.8701477050781241 +0.399 0.46673583984375 0.060546875 -0.8701477050781241 +0.399125 0.46856689453125 0.060791015625 -0.8701477050781241 +0.39925 0.470367431640625 0.06103515625 -0.8701477050781241 +0.3993750000000000512 0.470367431640625 0.06103515625 -0.8701477050781241 +0.3995 0.472137451171875 0.061248779296875 -0.8701477050781241 +0.399625 0.472137451171875 0.061248779296875 -0.8701477050781241 +0.39975 0.47381591796875 0.06146240234375 -0.8701477050781241 +0.3998750000000000512 0.4754638671875 0.061676025390625 -0.8701477050781241 +0.4 0.4754638671875 0.061676025390625 -0.8701477050781241 +0.400125 0.47705078125 0.0618896484375 -0.8701477050781241 +0.40025 0.47705078125 0.0618896484375 -0.8701477050781241 +0.400375 0.478607177734375 0.062103271484375 -0.8701477050781241 +0.4005 0.480072021484375 0.062286376953125 -0.8701477050781241 +0.400625 0.480072021484375 0.062286376953125 -0.8701477050781241 +0.40075 0.48150634765625 0.062469482421875 -0.8701477050781241 +0.400875 0.48150634765625 0.062469482421875 -0.8701477050781241 +0.401 0.48291015625 0.062652587890625 -0.8701477050781241 +0.401125 0.484222412109375 0.062835693359375 -0.8701477050781241 +0.40125 0.484222412109375 0.062835693359375 -0.8701477050781241 +0.401375 0.485504150390625 0.06298828125 -0.8701477050781241 +0.4015 0.485504150390625 0.06298828125 -0.8701477050781241 +0.401625 0.486724853515625 0.063140869140625 -0.8701477050781241 +0.40175 0.487884521484375 0.06329345703125 -0.8701477050781241 +0.401875 0.487884521484375 0.06329345703125 -0.8701477050781241 +0.402 0.489013671875 0.063446044921875 -0.8701477050781241 +0.402125 0.489013671875 0.063446044921875 -0.8701477050781241 +0.40225 0.490081787109375 0.063568115234375 -0.8701477050781241 +0.402375 0.4910888671875 0.063720703125 -0.8701477050781241 +0.4025 0.4910888671875 0.063720703125 -0.8701477050781241 +0.402625 0.492034912109375 0.0638427734375 -0.8701477050781241 +0.40275 0.492034912109375 0.0638427734375 -0.8701477050781241 +0.402875 0.492950439453125 0.06396484375 -0.8701477050781241 +0.403 0.4937744140625 0.064056396484375 -0.8701477050781241 +0.403125 0.4937744140625 0.064056396484375 -0.8701477050781241 +0.40325 0.49456787109375 0.064178466796875 -0.8701477050781241 +0.403375 0.49456787109375 0.064178466796875 -0.8701477050781241 +0.4035 0.49530029296875 0.06427001953125 -0.8701477050781241 +0.403625 0.496002197265625 0.064361572265625 -0.8701477050781241 +0.40375 0.496002197265625 0.064361572265625 -0.8701477050781241 +0.403875 0.496612548828125 0.064422607421875 -0.8701477050781241 +0.404 0.496612548828125 0.064422607421875 -0.8701477050781241 +0.404125 0.4971923828125 0.06451416015625 -0.8701477050781241 +0.40425 0.497711181640625 0.0645751953125 -0.8701477050781241 +0.404375 0.497711181640625 0.0645751953125 -0.8701477050781241 +0.4045 0.498199462890625 0.06463623046875 -0.8701477050781241 +0.404625 0.498199462890625 0.06463623046875 -0.8701477050781241 +0.40475 0.49859619140625 0.064697265625 -0.8701477050781241 +0.404875 0.49896240234375 0.064727783203125 -0.8701477050781241 +0.405 0.49896240234375 0.064727783203125 -0.8701477050781241 +0.405125 0.499267578125 0.064788818359375 -0.8701477050781241 +0.40525 0.499267578125 0.064788818359375 -0.8701477050781241 +0.405375 0.49951171875 0.0648193359375 -0.8701477050781241 +0.4055 0.49969482421875 0.0648193359375 -0.8701477050781241 +0.405625 0.49969482421875 0.0648193359375 -0.8701477050781241 +0.40575 0.49981689453125 0.064849853515625 -0.8701477050781241 +0.405875 0.49981689453125 0.064849853515625 -0.8701477050781241 +0.406 0.499908447265625 0.064849853515625 -0.8701477050781241 +0.406125 0.49993896484375 0.064849853515625 -0.8701477050781241 +0.40625 0.49993896484375 0.064849853515625 -0.8701477050781241 +0.406375 0.499908447265625 0.064849853515625 -0.8701477050781241 +0.4065000000000000512 0.499908447265625 0.064849853515625 -0.8701477050781241 +0.406625 0.49981689453125 0.064849853515625 -0.8701477050781241 +0.40675 0.49969482421875 0.0648193359375 -0.8701477050781241 +0.406875 0.49969482421875 0.0648193359375 -0.8701477050781241 +0.4070000000000000512 0.49951171875 0.0648193359375 -0.8701477050781241 +0.407125 0.49951171875 0.0648193359375 -0.8701477050781241 +0.40725 0.499267578125 0.064788818359375 -0.8701477050781241 +0.407375 0.49896240234375 0.064727783203125 -0.8701477050781241 +0.4075000000000000512 0.49896240234375 0.064727783203125 -0.8701477050781241 +0.407625 0.49859619140625 0.064697265625 -0.8701477050781241 +0.40775 0.49859619140625 0.064697265625 -0.8701477050781241 +0.407875 0.498199462890625 0.06463623046875 -0.8701477050781241 +0.4080000000000000512 0.497711181640625 0.0645751953125 -0.8701477050781241 +0.408125 0.497711181640625 0.0645751953125 -0.8701477050781241 +0.40825 0.4971923828125 0.06451416015625 -0.8701477050781241 +0.408375 0.4971923828125 0.06451416015625 -0.8701477050781241 +0.4085000000000000512 0.496612548828125 0.064422607421875 -0.8701477050781241 +0.408625 0.496002197265625 0.064361572265625 -0.8701477050781241 +0.40875 0.496002197265625 0.064361572265625 -0.8701477050781241 +0.408875 0.49530029296875 0.06427001953125 -0.8701477050781241 +0.4090000000000000512 0.49530029296875 0.06427001953125 -0.8701477050781241 +0.409125 0.49456787109375 0.064178466796875 -0.8701477050781241 +0.40925 0.4937744140625 0.064056396484375 -0.8701477050781241 +0.409375 0.4937744140625 0.064056396484375 -0.8701477050781241 +0.4095000000000000512 0.492950439453125 0.06396484375 -0.8701477050781241 +0.409625 0.492950439453125 0.06396484375 -0.8701477050781241 +0.40975 0.492034912109375 0.0638427734375 -0.8701477050781241 +0.409875 0.4910888671875 0.063720703125 -0.8701477050781241 +0.4100000000000000512 0.4910888671875 0.063720703125 -0.8701477050781241 +0.410125 0.490081787109375 0.063568115234375 -0.8701477050781241 +0.41025 0.490081787109375 0.063568115234375 -0.8701477050781241 +0.410375 0.489013671875 0.063446044921875 -0.8701477050781241 +0.4105000000000000512 0.487884521484375 0.06329345703125 -0.8701477050781241 +0.410625 0.487884521484375 0.06329345703125 -0.8701477050781241 +0.41075 0.486724853515625 0.063140869140625 -0.8701477050781241 +0.410875 0.486724853515625 0.063140869140625 -0.8701477050781241 +0.4110000000000000512 0.485504150390625 0.06298828125 -0.8701477050781241 +0.411125 0.484222412109375 0.062835693359375 -0.8701477050781241 +0.41125 0.484222412109375 0.062835693359375 -0.8701477050781241 +0.411375 0.48291015625 0.062652587890625 -0.8701477050781241 +0.4115000000000000512 0.48291015625 0.062652587890625 -0.8701477050781241 +0.411625 0.48150634765625 0.062469482421875 -0.8701477050781241 +0.41175 0.480072021484375 0.062286376953125 -0.8701477050781241 +0.411875 0.480072021484375 0.062286376953125 -0.8701477050781241 +0.4120000000000000512 0.478607177734375 0.062103271484375 -0.8701477050781241 +0.412125 0.478607177734375 0.062103271484375 -0.8701477050781241 +0.41225 0.47705078125 0.0618896484375 -0.8701477050781241 +0.412375 0.4754638671875 0.061676025390625 -0.8701477050781241 +0.4125000000000000512 0.4754638671875 0.061676025390625 -0.8701477050781241 +0.412625 0.47381591796875 0.06146240234375 -0.8701477050781241 +0.41275 0.47381591796875 0.06146240234375 -0.8701477050781241 +0.412875 0.472137451171875 0.061248779296875 -0.8701477050781241 +0.4130000000000000512 0.470367431640625 0.06103515625 -0.8701477050781241 +0.413125 0.470367431640625 0.06103515625 -0.8701477050781241 +0.41325 0.46856689453125 0.060791015625 -0.8701477050781241 +0.413375 0.46856689453125 0.060791015625 -0.8701477050781241 +0.4135000000000000512 0.46673583984375 0.060546875 -0.8701477050781241 +0.413625 0.464813232421875 0.060302734375 -0.8701477050781241 +0.41375 0.464813232421875 0.060302734375 -0.8701477050781241 +0.413875 0.462890625 0.06005859375 -0.8701477050781241 +0.4140000000000000512 0.462890625 0.06005859375 -0.8701477050781241 +0.414125 0.46087646484375 0.059783935546875 -0.8701477050781241 +0.41425 0.458831787109375 0.059539794921875 -0.8701477050781241 +0.414375 0.458831787109375 0.059539794921875 -0.8701477050781241 +0.4145000000000000512 0.45672607421875 0.05926513671875 -0.8701477050781241 +0.414625 0.45672607421875 0.05926513671875 -0.8701477050781241 +0.41475 0.454559326171875 0.0589599609375 -0.8701477050781241 +0.414875 0.452362060546875 0.058685302734375 -0.8701477050781241 +0.4150000000000000512 0.452362060546875 0.058685302734375 -0.8701477050781241 +0.415125 0.450103759765625 0.058380126953125 -0.8701477050781241 +0.41525 0.450103759765625 0.058380126953125 -0.8701477050781241 +0.415375 0.447784423828125 0.05810546875 -0.8701477050781241 +0.4155000000000000512 0.4454345703125 0.05780029296875 -0.8701477050781241 +0.415625 0.4454345703125 0.05780029296875 -0.8701477050781241 +0.41575 0.44305419921875 0.057464599609375 -0.8701477050781241 +0.415875 0.44305419921875 0.057464599609375 -0.8701477050781241 +0.416000000000000036 0.440582275390625 0.1727294921875 -0.6078857421874979 +0.416125 0.438079833984375 0.1717529296875 -0.6078857421874979 +0.41625 0.438079833984375 0.1717529296875 -0.6078857421874979 +0.416375 0.435546875 0.170745849609375 -0.6078857421874979 +0.4165 0.435546875 0.170745849609375 -0.6078857421874979 +0.416625 0.432952880859375 0.16973876953125 -0.6078857421874979 +0.41675 0.4302978515625 0.168701171875 -0.6078857421874979 +0.416875 0.4302978515625 0.168701171875 -0.6078857421874979 +0.417 0.4276123046875 0.167633056640625 -0.6078857421874979 +0.417125 0.4276123046875 0.167633056640625 -0.6078857421874979 +0.41725 0.424896240234375 0.16656494140625 -0.6078857421874979 +0.417375 0.422119140625 0.16546630859375 -0.6078857421874979 +0.4175 0.422119140625 0.16546630859375 -0.6078857421874979 +0.417625 0.419281005859375 0.16436767578125 -0.6078857421874979 +0.41775 0.419281005859375 0.16436767578125 -0.6078857421874979 +0.417875 0.416412353515625 0.163238525390625 -0.6078857421874979 +0.418 0.413482666015625 0.162078857421875 -0.6078857421874979 +0.418125 0.413482666015625 0.162078857421875 -0.6078857421874979 +0.41825 0.4105224609375 0.160919189453125 -0.6078857421874979 +0.418375 0.4105224609375 0.160919189453125 -0.6078857421874979 +0.4185 0.407501220703125 0.159759521484375 -0.6078857421874979 +0.418625 0.404449462890625 0.1585693359375 -0.6078857421874979 +0.41875 0.404449462890625 0.1585693359375 -0.6078857421874979 +0.418875 0.401336669921875 0.1573486328125 -0.6078857421874979 +0.419 0.401336669921875 0.1573486328125 -0.6078857421874979 +0.419125 0.398193359375 0.156097412109375 -0.6078857421874979 +0.41925 0.39501953125 0.154876708984375 -0.6078857421874979 +0.419375 0.39501953125 0.154876708984375 -0.6078857421874979 +0.4195 0.39178466796875 0.153594970703125 -0.6078857421874979 +0.419625 0.39178466796875 0.153594970703125 -0.6078857421874979 +0.41975 0.388519287109375 0.152313232421875 -0.6078857421874979 +0.419875 0.38519287109375 0.1510009765625 -0.6078857421874979 +0.42 0.38519287109375 0.1510009765625 -0.6078857421874979 +0.420125 0.3818359375 0.149688720703125 -0.6078857421874979 +0.42025 0.3818359375 0.149688720703125 -0.6078857421874979 +0.420375 0.378448486328125 0.148345947265625 -0.6078857421874979 +0.4205 0.375 0.147003173828125 -0.6078857421874979 +0.420625 0.375 0.147003173828125 -0.6078857421874979 +0.42075 0.37152099609375 0.1456298828125 -0.6078857421874979 +0.420875 0.37152099609375 0.1456298828125 -0.6078857421874979 +0.421 0.36798095703125 0.144256591796875 -0.6078857421874979 +0.421125 0.36444091796875 0.14288330078125 -0.6078857421874979 +0.42125 0.36444091796875 0.14288330078125 -0.6078857421874979 +0.421375 0.360809326171875 0.141448974609375 -0.6078857421874979 +0.4215 0.360809326171875 0.141448974609375 -0.6078857421874979 +0.421625 0.357177734375 0.1400146484375 -0.6078857421874979 +0.42175 0.353485107421875 0.138580322265625 -0.6078857421874979 +0.421875 0.353485107421875 0.138580322265625 -0.6078857421874979 +0.422 0.349761962890625 0.137115478515625 -0.6078857421874979 +0.4221250000000000512 0.349761962890625 0.137115478515625 -0.6078857421874979 +0.42225 0.34600830078125 0.135650634765625 -0.6078857421874979 +0.422375 0.34222412109375 0.1341552734375 -0.6078857421874979 +0.4225 0.34222412109375 0.1341552734375 -0.6078857421874979 +0.4226250000000000512 0.33837890625 0.132659912109375 -0.6078857421874979 +0.42275 0.33837890625 0.132659912109375 -0.6078857421874979 +0.422875 0.334503173828125 0.131134033203125 -0.6078857421874979 +0.423 0.330596923828125 0.129608154296875 -0.6078857421874979 +0.4231250000000000512 0.330596923828125 0.129608154296875 -0.6078857421874979 +0.42325 0.32666015625 0.1280517578125 -0.6078857421874979 +0.423375 0.32666015625 0.1280517578125 -0.6078857421874979 +0.4235 0.322662353515625 0.126495361328125 -0.6078857421874979 +0.4236250000000000512 0.31866455078125 0.124908447265625 -0.6078857421874979 +0.42375 0.31866455078125 0.124908447265625 -0.6078857421874979 +0.423875 0.314605712890625 0.123321533203125 -0.6078857421874979 +0.424 0.314605712890625 0.123321533203125 -0.6078857421874979 +0.4241250000000000512 0.310516357421875 0.121734619140625 -0.6078857421874979 +0.42425 0.306396484375 0.1201171875 -0.6078857421874979 +0.424375 0.306396484375 0.1201171875 -0.6078857421874979 +0.4245 0.30224609375 0.118499755859375 -0.6078857421874979 +0.4246250000000000512 0.30224609375 0.118499755859375 -0.6078857421874979 +0.42475 0.298065185546875 0.116851806640625 -0.6078857421874979 +0.424875 0.2938232421875 0.115203857421875 -0.6078857421874979 +0.425 0.2938232421875 0.115203857421875 -0.6078857421874979 +0.4251250000000000512 0.289581298828125 0.113525390625 -0.6078857421874979 +0.42525 0.289581298828125 0.113525390625 -0.6078857421874979 +0.425375 0.285308837890625 0.111846923828125 -0.6078857421874979 +0.4255 0.280975341796875 0.110137939453125 -0.6078857421874979 +0.4256250000000000512 0.280975341796875 0.110137939453125 -0.6078857421874979 +0.42575 0.276641845703125 0.10845947265625 -0.6078857421874979 +0.425875 0.276641845703125 0.10845947265625 -0.6078857421874979 +0.426 0.27227783203125 0.10675048828125 -0.6078857421874979 +0.4261250000000000512 0.267852783203125 0.105010986328125 -0.6078857421874979 +0.42625 0.267852783203125 0.105010986328125 -0.6078857421874979 +0.426375 0.263427734375 0.103271484375 -0.6078857421874979 +0.4265 0.263427734375 0.103271484375 -0.6078857421874979 +0.4266250000000000512 0.25897216796875 0.101531982421875 -0.6078857421874979 +0.42675 0.25445556640625 0.099761962890625 -0.6078857421874979 +0.426875 0.25445556640625 0.099761962890625 -0.6078857421874979 +0.427 0.249969482421875 0.097991943359375 -0.6078857421874979 +0.4271250000000000512 0.249969482421875 0.097991943359375 -0.6078857421874979 +0.42725 0.24542236328125 0.09619140625 -0.6078857421874979 +0.427375 0.2408447265625 0.09442138671875 -0.6078857421874979 +0.4275 0.2408447265625 0.09442138671875 -0.6078857421874979 +0.4276250000000000512 0.236236572265625 0.092620849609375 -0.6078857421874979 +0.42775 0.236236572265625 0.092620849609375 -0.6078857421874979 +0.427875 0.231597900390625 0.090789794921875 -0.6078857421874979 +0.428 0.226959228515625 0.088958740234375 -0.6078857421874979 +0.4281250000000000512 0.226959228515625 0.088958740234375 -0.6078857421874979 +0.42825 0.2222900390625 0.087127685546875 -0.6078857421874979 +0.428375 0.2222900390625 0.087127685546875 -0.6078857421874979 +0.4285 0.21759033203125 0.085296630859375 -0.6078857421874979 +0.4286250000000000512 0.212860107421875 0.08343505859375 -0.6078857421874979 +0.42875 0.212860107421875 0.08343505859375 -0.6078857421874979 +0.428875 0.208099365234375 0.081573486328125 -0.6078857421874979 +0.429 0.208099365234375 0.081573486328125 -0.6078857421874979 +0.4291250000000000512 0.203338623046875 0.0797119140625 -0.6078857421874979 +0.42925 0.19854736328125 0.07781982421875 -0.6078857421874979 +0.429375 0.19854736328125 0.07781982421875 -0.6078857421874979 +0.4295 0.1937255859375 0.075927734375 -0.6078857421874979 +0.4296250000000000512 0.1937255859375 0.075927734375 -0.6078857421874979 +0.42975 0.188873291015625 0.07403564453125 -0.6078857421874979 +0.429875 0.18402099609375 0.0721435546875 -0.6078857421874979 +0.43 0.18402099609375 0.0721435546875 -0.6078857421874979 +0.4301250000000000512 0.17913818359375 0.070220947265625 -0.6078857421874979 +0.43025 0.17913818359375 0.070220947265625 -0.6078857421874979 +0.430375 0.17425537109375 0.06829833984375 -0.6078857421874979 +0.4305 0.169342041015625 0.066375732421875 -0.6078857421874979 +0.4306250000000000512 0.169342041015625 0.066375732421875 -0.6078857421874979 +0.43075 0.164398193359375 0.064453125 -0.6078857421874979 +0.430875 0.164398193359375 0.064453125 -0.6078857421874979 +0.431 0.159454345703125 0.0625 -0.6078857421874979 +0.4311250000000000512 0.15447998046875 0.060546875 -0.6078857421874979 +0.43125 0.15447998046875 0.060546875 -0.6078857421874979 +0.431375 0.14947509765625 0.05859375 -0.6078857421874979 +0.4315 0.14947509765625 0.05859375 -0.6078857421874979 +0.431625000000000036 0.14447021484375 0.056640625 -0.6078857421874979 +0.43175 0.13946533203125 0.054656982421875 -0.6078857421874979 +0.431875 0.13946533203125 0.054656982421875 -0.6078857421874979 +0.432 0.134429931640625 0.052703857421875 -0.6078857421874979 +0.432125 0.134429931640625 0.052703857421875 -0.6078857421874979 +0.43225 0.129364013671875 0.05072021484375 -0.6078857421874979 +0.432375 0.124298095703125 0.048736572265625 -0.6078857421874979 +0.4325 0.124298095703125 0.048736572265625 -0.6078857421874979 +0.432625 0.119232177734375 0.0467529296875 -0.6078857421874979 +0.43275 0.119232177734375 0.0467529296875 -0.6078857421874979 +0.432875 0.1141357421875 0.04473876953125 -0.6078857421874979 +0.433 0.109039306640625 0.042724609375 -0.6078857421874979 +0.433125 0.109039306640625 0.042724609375 -0.6078857421874979 +0.43325 0.103912353515625 0.040740966796875 -0.6078857421874979 +0.433375 0.103912353515625 0.040740966796875 -0.6078857421874979 +0.4335 0.098785400390625 0.038726806640625 -0.6078857421874979 +0.433625 0.093658447265625 0.036712646484375 -0.6078857421874979 +0.43375 0.093658447265625 0.036712646484375 -0.6078857421874979 +0.433875 0.0885009765625 0.034698486328125 -0.6078857421874979 +0.434 0.0885009765625 0.034698486328125 -0.6078857421874979 +0.434125 0.083343505859375 0.032684326171875 -0.6078857421874979 +0.43425 0.07818603515625 0.0306396484375 -0.6078857421874979 +0.434375 0.07818603515625 0.0306396484375 -0.6078857421874979 +0.4345 0.072998046875 0.02862548828125 -0.6078857421874979 +0.434625 0.072998046875 0.02862548828125 -0.6078857421874979 +0.43475 0.06781005859375 0.026580810546875 -0.6078857421874979 +0.434875 0.0626220703125 0.0245361328125 -0.6078857421874979 +0.435 0.0626220703125 0.0245361328125 -0.6078857421874979 +0.435125 0.05743408203125 0.02252197265625 -0.6078857421874979 +0.43525 0.05743408203125 0.02252197265625 -0.6078857421874979 +0.435375 0.05224609375 0.020477294921875 -0.6078857421874979 +0.4355 0.047027587890625 0.0184326171875 -0.6078857421874979 +0.435625 0.047027587890625 0.0184326171875 -0.6078857421874979 +0.43575 0.04180908203125 0.016387939453125 -0.6078857421874979 +0.435875 0.04180908203125 0.016387939453125 -0.6078857421874979 +0.436 0.036590576171875 0.01434326171875 -0.6078857421874979 +0.436125 0.0313720703125 0.012298583984375 -0.6078857421874979 +0.43625 0.0313720703125 0.012298583984375 -0.6078857421874979 +0.436375 0.026123046875 0.010223388671875 -0.6078857421874979 +0.4365 0.026123046875 0.010223388671875 -0.6078857421874979 +0.436625 0.020904541015625 0.0081787109375 -0.6078857421874979 +0.43675 0.01568603515625 0.006134033203125 -0.6078857421874979 +0.436875 0.01568603515625 0.006134033203125 -0.6078857421874979 +0.437 0.01043701171875 0.00408935546875 -0.6078857421874979 +0.437125 0.01043701171875 0.00408935546875 -0.6078857421874979 +0.43725 0.005218505859375 0.002044677734375 -0.6078857421874979 +0.437375 0.0 0.0 -0.6078857421874979 +0.4375 0.0 0.0 -0.6078857421874979 +0.437625 -0.005218505859375 -0.002044677734375 -0.6078857421874979 +0.4377500000000000512 -0.005218505859375 -0.002044677734375 -0.6078857421874979 +0.437875 -0.01043701171875 -0.00408935546875 -0.6078857421874979 +0.438 -0.01568603515625 -0.006134033203125 -0.6078857421874979 +0.438125 -0.01568603515625 -0.006134033203125 -0.6078857421874979 +0.4382500000000000512 -0.020904541015625 -0.0081787109375 -0.6078857421874979 +0.438375 -0.020904541015625 -0.0081787109375 -0.6078857421874979 +0.4385 -0.026123046875 -0.010223388671875 -0.6078857421874979 +0.438625 -0.0313720703125 -0.012298583984375 -0.6078857421874979 +0.4387500000000000512 -0.0313720703125 -0.012298583984375 -0.6078857421874979 +0.438875 -0.036590576171875 -0.01434326171875 -0.6078857421874979 +0.439 -0.036590576171875 -0.01434326171875 -0.6078857421874979 +0.439125 -0.04180908203125 -0.016387939453125 -0.6078857421874979 +0.4392500000000000512 -0.047027587890625 -0.0184326171875 -0.6078857421874979 +0.439375 -0.047027587890625 -0.0184326171875 -0.6078857421874979 +0.4395 -0.05224609375 -0.020477294921875 -0.6078857421874979 +0.439625 -0.05224609375 -0.020477294921875 -0.6078857421874979 +0.4397500000000000512 -0.05743408203125 -0.02252197265625 -0.6078857421874979 +0.439875 -0.0626220703125 -0.0245361328125 -0.6078857421874979 +0.44 -0.0626220703125 -0.0245361328125 -0.6078857421874979 +0.440125 -0.06781005859375 -0.026580810546875 -0.6078857421874979 +0.4402500000000000512 -0.06781005859375 -0.026580810546875 -0.6078857421874979 +0.440375 -0.072998046875 -0.02862548828125 -0.6078857421874979 +0.4405 -0.07818603515625 -0.0306396484375 -0.6078857421874979 +0.440625 -0.07818603515625 -0.0306396484375 -0.6078857421874979 +0.4407500000000000512 -0.083343505859375 -0.032684326171875 -0.6078857421874979 +0.440875 -0.083343505859375 -0.032684326171875 -0.6078857421874979 +0.441 -0.0885009765625 -0.034698486328125 -0.6078857421874979 +0.441125 -0.093658447265625 -0.036712646484375 -0.6078857421874979 +0.4412500000000000512 -0.093658447265625 -0.036712646484375 -0.6078857421874979 +0.441375 -0.098785400390625 -0.038726806640625 -0.6078857421874979 +0.4415 -0.098785400390625 -0.038726806640625 -0.6078857421874979 +0.441625 -0.103912353515625 -0.040740966796875 -0.6078857421874979 +0.4417500000000000512 -0.109039306640625 -0.042724609375 -0.6078857421874979 +0.441875 -0.109039306640625 -0.042724609375 -0.6078857421874979 +0.442 -0.1141357421875 -0.04473876953125 -0.6078857421874979 +0.442125 -0.1141357421875 -0.04473876953125 -0.6078857421874979 +0.4422500000000000512 -0.119232177734375 -0.0467529296875 -0.6078857421874979 +0.442375 -0.124298095703125 -0.048736572265625 -0.6078857421874979 +0.4425 -0.124298095703125 -0.048736572265625 -0.6078857421874979 +0.442625 -0.129364013671875 -0.05072021484375 -0.6078857421874979 +0.4427500000000000512 -0.129364013671875 -0.05072021484375 -0.6078857421874979 +0.442875 -0.134429931640625 -0.052703857421875 -0.6078857421874979 +0.443 -0.13946533203125 -0.054656982421875 -0.6078857421874979 +0.443125 -0.13946533203125 -0.054656982421875 -0.6078857421874979 +0.4432500000000000512 -0.14447021484375 -0.056640625 -0.6078857421874979 +0.443375 -0.14447021484375 -0.056640625 -0.6078857421874979 +0.4435 -0.14947509765625 -0.05859375 -0.6078857421874979 +0.443625 -0.15447998046875 -0.060546875 -0.6078857421874979 +0.4437500000000000512 -0.15447998046875 -0.060546875 -0.6078857421874979 +0.443875 -0.159454345703125 -0.0625 -0.6078857421874979 +0.444 -0.159454345703125 -0.0625 -0.6078857421874979 +0.444125 -0.164398193359375 -0.064453125 -0.6078857421874979 +0.4442500000000000512 -0.169342041015625 -0.066375732421875 -0.6078857421874979 +0.444375 -0.169342041015625 -0.066375732421875 -0.6078857421874979 +0.4445 -0.17425537109375 -0.06829833984375 -0.6078857421874979 +0.444625 -0.17425537109375 -0.06829833984375 -0.6078857421874979 +0.4447500000000000512 -0.17913818359375 -0.070220947265625 -0.6078857421874979 +0.444875 -0.18402099609375 -0.0721435546875 -0.6078857421874979 +0.445 -0.18402099609375 -0.0721435546875 -0.6078857421874979 +0.445125 -0.188873291015625 -0.07403564453125 -0.6078857421874979 +0.4452500000000000512 -0.188873291015625 -0.07403564453125 -0.6078857421874979 +0.445375 -0.1937255859375 -0.075927734375 -0.6078857421874979 +0.4455 -0.19854736328125 -0.07781982421875 -0.6078857421874979 +0.445625 -0.19854736328125 -0.07781982421875 -0.6078857421874979 +0.4457500000000000512 -0.203338623046875 -0.0797119140625 -0.6078857421874979 +0.445875 -0.203338623046875 -0.0797119140625 -0.6078857421874979 +0.446 -0.208099365234375 -0.081573486328125 -0.6078857421874979 +0.446125 -0.212860107421875 -0.08343505859375 -0.6078857421874979 +0.4462500000000000512 -0.212860107421875 -0.08343505859375 -0.6078857421874979 +0.446375 -0.21759033203125 -0.085296630859375 -0.6078857421874979 +0.4465 -0.21759033203125 -0.085296630859375 -0.6078857421874979 +0.446625 -0.2222900390625 -0.087127685546875 -0.6078857421874979 +0.4467500000000000512 -0.226959228515625 -0.088958740234375 -0.6078857421874979 +0.446875 -0.226959228515625 -0.088958740234375 -0.6078857421874979 +0.447 -0.231597900390625 -0.090789794921875 -0.6078857421874979 +0.447125 -0.231597900390625 -0.090789794921875 -0.6078857421874979 +0.447250000000000036 -0.236236572265625 -0.092620849609375 -0.6078857421874979 +0.447375 -0.2408447265625 -0.09442138671875 -0.6078857421874979 +0.4475 -0.2408447265625 -0.09442138671875 -0.6078857421874979 +0.447625 -0.24542236328125 -0.09619140625 -0.6078857421874979 +0.447750000000000036 -0.24542236328125 -0.09619140625 -0.6078857421874979 +0.447875 -0.249969482421875 -0.097991943359375 -0.6078857421874979 +0.448 -0.25445556640625 -0.191192626953125 -0.2486572265624977 +0.448125 -0.25445556640625 -0.191192626953125 -0.2486572265624977 +0.44825 -0.25897216796875 -0.194549560546875 -0.2486572265624977 +0.448375 -0.25897216796875 -0.194549560546875 -0.2486572265624977 +0.4485 -0.263427734375 -0.197906494140625 -0.2486572265624977 +0.448625 -0.267852783203125 -0.20123291015625 -0.2486572265624977 +0.44875 -0.267852783203125 -0.20123291015625 -0.2486572265624977 +0.448875 -0.27227783203125 -0.204559326171875 -0.2486572265624977 +0.449 -0.27227783203125 -0.204559326171875 -0.2486572265624977 +0.449125 -0.276641845703125 -0.20782470703125 -0.2486572265624977 +0.44925 -0.280975341796875 -0.211090087890625 -0.2486572265624977 +0.449375 -0.280975341796875 -0.211090087890625 -0.2486572265624977 +0.4495 -0.285308837890625 -0.21435546875 -0.2486572265624977 +0.449625 -0.285308837890625 -0.21435546875 -0.2486572265624977 +0.44975 -0.289581298828125 -0.217559814453125 -0.2486572265624977 +0.449875 -0.2938232421875 -0.22076416015625 -0.2486572265624977 +0.45 -0.2938232421875 -0.22076416015625 -0.2486572265624977 +0.450125 -0.298065185546875 -0.22393798828125 -0.2486572265624977 +0.45025 -0.298065185546875 -0.22393798828125 -0.2486572265624977 +0.450375 -0.30224609375 -0.227081298828125 -0.2486572265624977 +0.4505 -0.306396484375 -0.230194091796875 -0.2486572265624977 +0.450625 -0.306396484375 -0.230194091796875 -0.2486572265624977 +0.45075 -0.310516357421875 -0.233306884765625 -0.2486572265624977 +0.450875 -0.310516357421875 -0.233306884765625 -0.2486572265624977 +0.451 -0.314605712890625 -0.236358642578125 -0.2486572265624977 +0.451125 -0.31866455078125 -0.239410400390625 -0.2486572265624977 +0.45125 -0.31866455078125 -0.239410400390625 -0.2486572265624977 +0.451375 -0.322662353515625 -0.242431640625 -0.2486572265624977 +0.4515 -0.322662353515625 -0.242431640625 -0.2486572265624977 +0.451625 -0.32666015625 -0.24542236328125 -0.2486572265624977 +0.45175 -0.330596923828125 -0.248382568359375 -0.2486572265624977 +0.451875 -0.330596923828125 -0.248382568359375 -0.2486572265624977 +0.452 -0.334503173828125 -0.251312255859375 -0.2486572265624977 +0.452125 -0.334503173828125 -0.251312255859375 -0.2486572265624977 +0.45225 -0.33837890625 -0.254241943359375 -0.2486572265624977 +0.452375 -0.34222412109375 -0.257110595703125 -0.2486572265624977 +0.4525 -0.34222412109375 -0.257110595703125 -0.2486572265624977 +0.452625 -0.34600830078125 -0.25994873046875 -0.2486572265624977 +0.45275 -0.34600830078125 -0.25994873046875 -0.2486572265624977 +0.452875 -0.349761962890625 -0.262786865234375 -0.2486572265624977 +0.453 -0.353485107421875 -0.26556396484375 -0.2486572265624977 +0.453125 -0.353485107421875 -0.26556396484375 -0.2486572265624977 +0.45325 -0.357177734375 -0.268341064453125 -0.2486572265624977 +0.4533750000000000512 -0.357177734375 -0.268341064453125 -0.2486572265624977 +0.4535 -0.360809326171875 -0.271087646484375 -0.2486572265624977 +0.453625 -0.36444091796875 -0.2738037109375 -0.2486572265624977 +0.45375 -0.36444091796875 -0.2738037109375 -0.2486572265624977 +0.4538750000000000512 -0.36798095703125 -0.276458740234375 -0.2486572265624977 +0.454 -0.36798095703125 -0.276458740234375 -0.2486572265624977 +0.454125 -0.37152099609375 -0.27911376953125 -0.2486572265624977 +0.45425 -0.375 -0.28173828125 -0.2486572265624977 +0.4543750000000000512 -0.375 -0.28173828125 -0.2486572265624977 +0.4545 -0.378448486328125 -0.284332275390625 -0.2486572265624977 +0.454625 -0.378448486328125 -0.284332275390625 -0.2486572265624977 +0.45475 -0.3818359375 -0.286865234375 -0.2486572265624977 +0.4548750000000000512 -0.38519287109375 -0.289398193359375 -0.2486572265624977 +0.455 -0.38519287109375 -0.289398193359375 -0.2486572265624977 +0.455125 -0.388519287109375 -0.2918701171875 -0.2486572265624977 +0.45525 -0.388519287109375 -0.2918701171875 -0.2486572265624977 +0.4553750000000000512 -0.39178466796875 -0.294342041015625 -0.2486572265624977 +0.4555 -0.39501953125 -0.296783447265625 -0.2486572265624977 +0.455625 -0.39501953125 -0.296783447265625 -0.2486572265624977 +0.45575 -0.398193359375 -0.299163818359375 -0.2486572265624977 +0.4558750000000000512 -0.398193359375 -0.299163818359375 -0.2486572265624977 +0.456 -0.401336669921875 -0.301513671875 -0.2486572265624977 +0.456125 -0.404449462890625 -0.303863525390625 -0.2486572265624977 +0.45625 -0.404449462890625 -0.303863525390625 -0.2486572265624977 +0.4563750000000000512 -0.407501220703125 -0.30615234375 -0.2486572265624977 +0.4565 -0.407501220703125 -0.30615234375 -0.2486572265624977 +0.456625 -0.4105224609375 -0.30841064453125 -0.2486572265624977 +0.45675 -0.413482666015625 -0.310638427734375 -0.2486572265624977 +0.4568750000000000512 -0.413482666015625 -0.310638427734375 -0.2486572265624977 +0.457 -0.416412353515625 -0.312835693359375 -0.2486572265624977 +0.457125 -0.416412353515625 -0.312835693359375 -0.2486572265624977 +0.45725 -0.419281005859375 -0.31500244140625 -0.2486572265624977 +0.4573750000000000512 -0.422119140625 -0.317138671875 -0.2486572265624977 +0.4575 -0.422119140625 -0.317138671875 -0.2486572265624977 +0.457625 -0.424896240234375 -0.3192138671875 -0.2486572265624977 +0.45775 -0.424896240234375 -0.3192138671875 -0.2486572265624977 +0.4578750000000000512 -0.4276123046875 -0.321258544921875 -0.2486572265624977 +0.458 -0.4302978515625 -0.323272705078125 -0.2486572265624977 +0.458125 -0.4302978515625 -0.323272705078125 -0.2486572265624977 +0.45825 -0.432952880859375 -0.325286865234375 -0.2486572265624977 +0.4583750000000000512 -0.432952880859375 -0.325286865234375 -0.2486572265624977 +0.4585 -0.435546875 -0.32720947265625 -0.2486572265624977 +0.458625 -0.438079833984375 -0.329132080078125 -0.2486572265624977 +0.45875 -0.438079833984375 -0.329132080078125 -0.2486572265624977 +0.4588750000000000512 -0.440582275390625 -0.33099365234375 -0.2486572265624977 +0.459 -0.440582275390625 -0.33099365234375 -0.2486572265624977 +0.459125 -0.44305419921875 -0.332855224609375 -0.2486572265624977 +0.45925 -0.4454345703125 -0.33465576171875 -0.2486572265624977 +0.4593750000000000512 -0.4454345703125 -0.33465576171875 -0.2486572265624977 +0.4595 -0.447784423828125 -0.33642578125 -0.2486572265624977 +0.459625 -0.447784423828125 -0.33642578125 -0.2486572265624977 +0.45975 -0.450103759765625 -0.338134765625 -0.2486572265624977 +0.4598750000000000512 -0.452362060546875 -0.33984375 -0.2486572265624977 +0.46 -0.452362060546875 -0.33984375 -0.2486572265624977 +0.460125 -0.454559326171875 -0.34149169921875 -0.2486572265624977 +0.46025 -0.454559326171875 -0.34149169921875 -0.2486572265624977 +0.4603750000000000512 -0.45672607421875 -0.343109130859375 -0.2486572265624977 +0.4605 -0.458831787109375 -0.344696044921875 -0.2486572265624977 +0.460625 -0.458831787109375 -0.344696044921875 -0.2486572265624977 +0.46075 -0.46087646484375 -0.34625244140625 -0.2486572265624977 +0.4608750000000000512 -0.46087646484375 -0.34625244140625 -0.2486572265624977 +0.461 -0.462890625 -0.347747802734375 -0.2486572265624977 +0.461125 -0.464813232421875 -0.349212646484375 -0.2486572265624977 +0.46125 -0.464813232421875 -0.349212646484375 -0.2486572265624977 +0.461375000000000052 -0.46673583984375 -0.35064697265625 -0.2486572265624977 +0.4615 -0.46673583984375 -0.35064697265625 -0.2486572265624977 +0.461625 -0.46856689453125 -0.352020263671875 -0.2486572265624977 +0.46175 -0.470367431640625 -0.3533935546875 -0.2486572265624977 +0.461875000000000052 -0.470367431640625 -0.3533935546875 -0.2486572265624977 +0.462 -0.472137451171875 -0.354705810546875 -0.2486572265624977 +0.462125 -0.472137451171875 -0.354705810546875 -0.2486572265624977 +0.46225 -0.47381591796875 -0.355987548828125 -0.2486572265624977 +0.462375000000000052 -0.4754638671875 -0.357208251953125 -0.2486572265624977 +0.4625 -0.4754638671875 -0.357208251953125 -0.2486572265624977 +0.462625 -0.47705078125 -0.3583984375 -0.2486572265624977 +0.46275 -0.47705078125 -0.3583984375 -0.2486572265624977 +0.462875000000000064 -0.478607177734375 -0.35955810546875 -0.2486572265624977 +0.463 -0.480072021484375 -0.360687255859375 -0.2486572265624977 +0.463125 -0.480072021484375 -0.360687255859375 -0.2486572265624977 +0.46325 -0.48150634765625 -0.36175537109375 -0.2486572265624977 +0.463375000000000064 -0.48150634765625 -0.36175537109375 -0.2486572265624977 +0.4635 -0.48291015625 -0.36279296875 -0.2486572265624977 +0.463625 -0.484222412109375 -0.363800048828125 -0.2486572265624977 +0.46375 -0.484222412109375 -0.363800048828125 -0.2486572265624977 +0.463875000000000064 -0.485504150390625 -0.36474609375 -0.2486572265624977 +0.464 -0.485504150390625 -0.36474609375 -0.2486572265624977 +0.464125 -0.486724853515625 -0.36566162109375 -0.2486572265624977 +0.46425 -0.487884521484375 -0.366546630859375 -0.2486572265624977 +0.464375 -0.487884521484375 -0.366546630859375 -0.2486572265624977 +0.4645 -0.489013671875 -0.36737060546875 -0.2486572265624977 +0.464625 -0.489013671875 -0.36737060546875 -0.2486572265624977 +0.46475 -0.490081787109375 -0.368194580078125 -0.2486572265624977 +0.464875 -0.4910888671875 -0.368927001953125 -0.2486572265624977 +0.465 -0.4910888671875 -0.368927001953125 -0.2486572265624977 +0.465125 -0.492034912109375 -0.369659423828125 -0.2486572265624977 +0.46525 -0.492034912109375 -0.369659423828125 -0.2486572265624977 +0.465375 -0.492950439453125 -0.370330810546875 -0.2486572265624977 +0.4655 -0.4937744140625 -0.3709716796875 -0.2486572265624977 +0.465625 -0.4937744140625 -0.3709716796875 -0.2486572265624977 +0.46575 -0.49456787109375 -0.371551513671875 -0.2486572265624977 +0.465875 -0.49456787109375 -0.371551513671875 -0.2486572265624977 +0.466 -0.49530029296875 -0.372100830078125 -0.2486572265624977 +0.466125 -0.496002197265625 -0.37261962890625 -0.2486572265624977 +0.46625 -0.496002197265625 -0.37261962890625 -0.2486572265624977 +0.466375 -0.496612548828125 -0.37310791015625 -0.2486572265624977 +0.4665 -0.496612548828125 -0.37310791015625 -0.2486572265624977 +0.466625 -0.4971923828125 -0.37353515625 -0.2486572265624977 +0.46675 -0.497711181640625 -0.373931884765625 -0.2486572265624977 +0.466875 -0.497711181640625 -0.373931884765625 -0.2486572265624977 +0.467 -0.498199462890625 -0.374267578125 -0.2486572265624977 +0.467125 -0.498199462890625 -0.374267578125 -0.2486572265624977 +0.46725 -0.49859619140625 -0.374603271484375 -0.2486572265624977 +0.467375 -0.49896240234375 -0.374847412109375 -0.2486572265624977 +0.4675 -0.49896240234375 -0.374847412109375 -0.2486572265624977 +0.467625 -0.499267578125 -0.375091552734375 -0.2486572265624977 +0.46775 -0.499267578125 -0.375091552734375 -0.2486572265624977 +0.467875 -0.49951171875 -0.375274658203125 -0.2486572265624977 +0.468 -0.49969482421875 -0.375396728515625 -0.2486572265624977 +0.468125 -0.49969482421875 -0.375396728515625 -0.2486572265624977 +0.46825 -0.49981689453125 -0.375518798828125 -0.2486572265624977 +0.468375 -0.49981689453125 -0.375518798828125 -0.2486572265624977 +0.4685 -0.499908447265625 -0.375579833984375 -0.2486572265624977 +0.468625 -0.49993896484375 -0.3756103515625 -0.2486572265624977 +0.46875 -0.49993896484375 -0.3756103515625 -0.2486572265624977 +0.468875 -0.499908447265625 -0.375579833984375 -0.2486572265624977 +0.469000000000000052 -0.499908447265625 -0.375579833984375 -0.2486572265624977 +0.469125 -0.49981689453125 -0.375518798828125 -0.2486572265624977 +0.46925 -0.49969482421875 -0.375396728515625 -0.2486572265624977 +0.469375 -0.49969482421875 -0.375396728515625 -0.2486572265624977 +0.469500000000000052 -0.49951171875 -0.375274658203125 -0.2486572265624977 +0.469625 -0.49951171875 -0.375274658203125 -0.2486572265624977 +0.46975 -0.499267578125 -0.375091552734375 -0.2486572265624977 +0.469875 -0.49896240234375 -0.374847412109375 -0.2486572265624977 +0.470000000000000052 -0.49896240234375 -0.374847412109375 -0.2486572265624977 +0.470125 -0.49859619140625 -0.374603271484375 -0.2486572265624977 +0.47025 -0.49859619140625 -0.374603271484375 -0.2486572265624977 +0.470375 -0.498199462890625 -0.374267578125 -0.2486572265624977 +0.470500000000000052 -0.497711181640625 -0.373931884765625 -0.2486572265624977 +0.470625 -0.497711181640625 -0.373931884765625 -0.2486572265624977 +0.47075 -0.4971923828125 -0.37353515625 -0.2486572265624977 +0.470875 -0.4971923828125 -0.37353515625 -0.2486572265624977 +0.471000000000000052 -0.496612548828125 -0.37310791015625 -0.2486572265624977 +0.471125 -0.496002197265625 -0.37261962890625 -0.2486572265624977 +0.47125 -0.496002197265625 -0.37261962890625 -0.2486572265624977 +0.471375 -0.49530029296875 -0.372100830078125 -0.2486572265624977 +0.471500000000000052 -0.49530029296875 -0.372100830078125 -0.2486572265624977 +0.471625 -0.49456787109375 -0.371551513671875 -0.2486572265624977 +0.47175 -0.4937744140625 -0.3709716796875 -0.2486572265624977 +0.471875 -0.4937744140625 -0.3709716796875 -0.2486572265624977 +0.472000000000000052 -0.492950439453125 -0.370330810546875 -0.2486572265624977 +0.472125 -0.492950439453125 -0.370330810546875 -0.2486572265624977 +0.47225 -0.492034912109375 -0.369659423828125 -0.2486572265624977 +0.472375 -0.4910888671875 -0.368927001953125 -0.2486572265624977 +0.472500000000000052 -0.4910888671875 -0.368927001953125 -0.2486572265624977 +0.472625 -0.490081787109375 -0.368194580078125 -0.2486572265624977 +0.47275 -0.490081787109375 -0.368194580078125 -0.2486572265624977 +0.472875 -0.489013671875 -0.36737060546875 -0.2486572265624977 +0.473000000000000052 -0.487884521484375 -0.366546630859375 -0.2486572265624977 +0.473125 -0.487884521484375 -0.366546630859375 -0.2486572265624977 +0.47325 -0.486724853515625 -0.36566162109375 -0.2486572265624977 +0.473375 -0.486724853515625 -0.36566162109375 -0.2486572265624977 +0.473500000000000052 -0.485504150390625 -0.36474609375 -0.2486572265624977 +0.473625 -0.484222412109375 -0.363800048828125 -0.2486572265624977 +0.47375 -0.484222412109375 -0.363800048828125 -0.2486572265624977 +0.473875 -0.48291015625 -0.36279296875 -0.2486572265624977 +0.474000000000000052 -0.48291015625 -0.36279296875 -0.2486572265624977 +0.474125 -0.48150634765625 -0.36175537109375 -0.2486572265624977 +0.47425 -0.480072021484375 -0.360687255859375 -0.2486572265624977 +0.474375 -0.480072021484375 -0.360687255859375 -0.2486572265624977 +0.474500000000000052 -0.478607177734375 -0.35955810546875 -0.2486572265624977 +0.474625 -0.478607177734375 -0.35955810546875 -0.2486572265624977 +0.47475 -0.47705078125 -0.3583984375 -0.2486572265624977 +0.474875 -0.4754638671875 -0.357208251953125 -0.2486572265624977 +0.475000000000000052 -0.4754638671875 -0.357208251953125 -0.2486572265624977 +0.475125 -0.47381591796875 -0.355987548828125 -0.2486572265624977 +0.47525 -0.47381591796875 -0.355987548828125 -0.2486572265624977 +0.475375 -0.472137451171875 -0.354705810546875 -0.2486572265624977 +0.475500000000000052 -0.470367431640625 -0.3533935546875 -0.2486572265624977 +0.475625 -0.470367431640625 -0.3533935546875 -0.2486572265624977 +0.47575 -0.46856689453125 -0.352020263671875 -0.2486572265624977 +0.475875 -0.46856689453125 -0.352020263671875 -0.2486572265624977 +0.476000000000000052 -0.46673583984375 -0.35064697265625 -0.2486572265624977 +0.476125 -0.464813232421875 -0.349212646484375 -0.2486572265624977 +0.47625 -0.464813232421875 -0.349212646484375 -0.2486572265624977 +0.476375 -0.462890625 -0.347747802734375 -0.2486572265624977 +0.476500000000000052 -0.462890625 -0.347747802734375 -0.2486572265624977 +0.476625 -0.46087646484375 -0.34625244140625 -0.2486572265624977 +0.47675 -0.458831787109375 -0.344696044921875 -0.2486572265624977 +0.476875 -0.458831787109375 -0.344696044921875 -0.2486572265624977 +0.477000000000000052 -0.45672607421875 -0.343109130859375 -0.2486572265624977 +0.477125 -0.45672607421875 -0.343109130859375 -0.2486572265624977 +0.47725 -0.454559326171875 -0.34149169921875 -0.2486572265624977 +0.477375 -0.452362060546875 -0.33984375 -0.2486572265624977 +0.477500000000000052 -0.452362060546875 -0.33984375 -0.2486572265624977 +0.477625 -0.450103759765625 -0.338134765625 -0.2486572265624977 +0.47775 -0.450103759765625 -0.338134765625 -0.2486572265624977 +0.477875 -0.447784423828125 -0.33642578125 -0.2486572265624977 +0.478000000000000052 -0.4454345703125 -0.33465576171875 -0.2486572265624977 +0.478125 -0.4454345703125 -0.33465576171875 -0.2486572265624977 +0.47825 -0.44305419921875 -0.332855224609375 -0.2486572265624977 +0.478375 -0.44305419921875 -0.332855224609375 -0.2486572265624977 +0.478500000000000064 -0.440582275390625 -0.33099365234375 -0.2486572265624977 +0.478625 -0.438079833984375 -0.329132080078125 -0.2486572265624977 +0.47875 -0.438079833984375 -0.329132080078125 -0.2486572265624977 +0.478875 -0.435546875 -0.32720947265625 -0.2486572265624977 +0.479000000000000064 -0.435546875 -0.32720947265625 -0.2486572265624977 +0.479125 -0.432952880859375 -0.325286865234375 -0.2486572265624977 +0.47925 -0.4302978515625 -0.323272705078125 -0.2486572265624977 +0.479375 -0.4302978515625 -0.323272705078125 -0.2486572265624977 +0.479500000000000064 -0.4276123046875 -0.321258544921875 -0.2486572265624977 +0.479625 -0.4276123046875 -0.321258544921875 -0.2486572265624977 +0.47975 -0.424896240234375 -0.3192138671875 -0.2486572265624977 +0.479875 -0.422119140625 -0.317138671875 -0.2486572265624977 +0.48 -0.358673095703125 -0.422119140625 0.1501953125000029 +0.480125 -0.35626220703125 -0.419281005859375 0.1501953125000029 +0.48025 -0.35626220703125 -0.419281005859375 0.1501953125000029 +0.480375 -0.353851318359375 -0.416412353515625 0.1501953125000029 +0.4805 -0.351348876953125 -0.413482666015625 0.1501953125000029 +0.480625 -0.351348876953125 -0.413482666015625 0.1501953125000029 +0.48075 -0.348846435546875 -0.4105224609375 0.1501953125000029 +0.480875 -0.348846435546875 -0.4105224609375 0.1501953125000029 +0.481 -0.346282958984375 -0.407501220703125 0.1501953125000029 +0.481125 -0.34368896484375 -0.404449462890625 0.1501953125000029 +0.48125 -0.34368896484375 -0.404449462890625 0.1501953125000029 +0.481375 -0.341033935546875 -0.401336669921875 0.1501953125000029 +0.4815 -0.341033935546875 -0.401336669921875 0.1501953125000029 +0.481625 -0.33837890625 -0.398193359375 0.1501953125000029 +0.48175 -0.335662841796875 -0.39501953125 0.1501953125000029 +0.481875 -0.335662841796875 -0.39501953125 0.1501953125000029 +0.482 -0.332916259765625 -0.39178466796875 0.1501953125000029 +0.482125 -0.332916259765625 -0.39178466796875 0.1501953125000029 +0.48225 -0.33013916015625 -0.388519287109375 0.1501953125000029 +0.482375 -0.32733154296875 -0.38519287109375 0.1501953125000029 +0.4825 -0.32733154296875 -0.38519287109375 0.1501953125000029 +0.482625 -0.324462890625 -0.3818359375 0.1501953125000029 +0.48275 -0.324462890625 -0.3818359375 0.1501953125000029 +0.482875 -0.321563720703125 -0.378448486328125 0.1501953125000029 +0.483 -0.318634033203125 -0.375 0.1501953125000029 +0.483125 -0.318634033203125 -0.375 0.1501953125000029 +0.48325 -0.315704345703125 -0.37152099609375 0.1501953125000029 +0.483375 -0.315704345703125 -0.37152099609375 0.1501953125000029 +0.4835 -0.31268310546875 -0.36798095703125 0.1501953125000029 +0.483625 -0.309661865234375 -0.36444091796875 0.1501953125000029 +0.48375 -0.309661865234375 -0.36444091796875 0.1501953125000029 +0.483875 -0.306610107421875 -0.360809326171875 0.1501953125000029 +0.484 -0.306610107421875 -0.360809326171875 0.1501953125000029 +0.484125 -0.30352783203125 -0.357177734375 0.1501953125000029 +0.48425 -0.300384521484375 -0.353485107421875 0.1501953125000029 +0.484375 -0.300384521484375 -0.353485107421875 0.1501953125000029 +0.4845 -0.297210693359375 -0.349761962890625 0.1501953125000029 +0.484625000000000052 -0.297210693359375 -0.349761962890625 0.1501953125000029 +0.48475 -0.294036865234375 -0.34600830078125 0.1501953125000029 +0.484875 -0.290802001953125 -0.34222412109375 0.1501953125000029 +0.485 -0.290802001953125 -0.34222412109375 0.1501953125000029 +0.485125000000000052 -0.28753662109375 -0.33837890625 0.1501953125000029 +0.48525 -0.28753662109375 -0.33837890625 0.1501953125000029 +0.485375 -0.28424072265625 -0.334503173828125 0.1501953125000029 +0.4855 -0.28094482421875 -0.330596923828125 0.1501953125000029 +0.485625000000000052 -0.28094482421875 -0.330596923828125 0.1501953125000029 +0.48575 -0.277587890625 -0.32666015625 0.1501953125000029 +0.485875 -0.277587890625 -0.32666015625 0.1501953125000029 +0.486 -0.274200439453125 -0.322662353515625 0.1501953125000029 +0.486125000000000052 -0.270782470703125 -0.31866455078125 0.1501953125000029 +0.48625 -0.270782470703125 -0.31866455078125 0.1501953125000029 +0.486375 -0.267333984375 -0.314605712890625 0.1501953125000029 +0.4865 -0.267333984375 -0.314605712890625 0.1501953125000029 +0.486625000000000052 -0.26385498046875 -0.310516357421875 0.1501953125000029 +0.48675 -0.2603759765625 -0.306396484375 0.1501953125000029 +0.486875 -0.2603759765625 -0.306396484375 0.1501953125000029 +0.487 -0.2568359375 -0.30224609375 0.1501953125000029 +0.487125000000000052 -0.2568359375 -0.30224609375 0.1501953125000029 +0.48725 -0.253265380859375 -0.298065185546875 0.1501953125000029 +0.487375 -0.24969482421875 -0.2938232421875 0.1501953125000029 +0.4875 -0.24969482421875 -0.2938232421875 0.1501953125000029 +0.487625000000000052 -0.24609375 -0.289581298828125 0.1501953125000029 +0.48775 -0.24609375 -0.289581298828125 0.1501953125000029 +0.487875 -0.242431640625 -0.285308837890625 0.1501953125000029 +0.488 -0.23876953125 -0.280975341796875 0.1501953125000029 +0.488125000000000052 -0.23876953125 -0.280975341796875 0.1501953125000029 +0.48825 -0.235076904296875 -0.276641845703125 0.1501953125000029 +0.488375 -0.235076904296875 -0.276641845703125 0.1501953125000029 +0.4885 -0.231353759765625 -0.27227783203125 0.1501953125000029 +0.488625000000000052 -0.227630615234375 -0.267852783203125 0.1501953125000029 +0.48875 -0.227630615234375 -0.267852783203125 0.1501953125000029 +0.488875 -0.223846435546875 -0.263427734375 0.1501953125000029 +0.489 -0.223846435546875 -0.263427734375 0.1501953125000029 +0.489125000000000052 -0.220062255859375 -0.25897216796875 0.1501953125000029 +0.48925 -0.21624755859375 -0.25445556640625 0.1501953125000029 +0.489375 -0.21624755859375 -0.25445556640625 0.1501953125000029 +0.4895 -0.21240234375 -0.249969482421875 0.1501953125000029 +0.489625000000000052 -0.21240234375 -0.249969482421875 0.1501953125000029 +0.48975 -0.208526611328125 -0.24542236328125 0.1501953125000029 +0.489875 -0.20465087890625 -0.2408447265625 0.1501953125000029 +0.49 -0.20465087890625 -0.2408447265625 0.1501953125000029 +0.490125000000000052 -0.20074462890625 -0.236236572265625 0.1501953125000029 +0.49025 -0.20074462890625 -0.236236572265625 0.1501953125000029 +0.490375 -0.196807861328125 -0.231597900390625 0.1501953125000029 +0.4905 -0.192840576171875 -0.226959228515625 0.1501953125000029 +0.490625000000000052 -0.192840576171875 -0.226959228515625 0.1501953125000029 +0.49075 -0.188873291015625 -0.2222900390625 0.1501953125000029 +0.490875 -0.188873291015625 -0.2222900390625 0.1501953125000029 +0.491 -0.18487548828125 -0.21759033203125 0.1501953125000029 +0.491125000000000052 -0.180877685546875 -0.212860107421875 0.1501953125000029 +0.49125 -0.180877685546875 -0.212860107421875 0.1501953125000029 +0.491375 -0.17681884765625 -0.208099365234375 0.1501953125000029 +0.4915 -0.17681884765625 -0.208099365234375 0.1501953125000029 +0.491625000000000052 -0.172760009765625 -0.203338623046875 0.1501953125000029 +0.49175 -0.168701171875 -0.19854736328125 0.1501953125000029 +0.491875 -0.168701171875 -0.19854736328125 0.1501953125000029 +0.492 -0.16461181640625 -0.1937255859375 0.1501953125000029 +0.492125000000000052 -0.16461181640625 -0.1937255859375 0.1501953125000029 +0.49225 -0.160491943359375 -0.188873291015625 0.1501953125000029 +0.492375 -0.1563720703125 -0.18402099609375 0.1501953125000029 +0.4925 -0.1563720703125 -0.18402099609375 0.1501953125000029 +0.492625000000000052 -0.1522216796875 -0.17913818359375 0.1501953125000029 +0.49275 -0.1522216796875 -0.17913818359375 0.1501953125000029 +0.492875 -0.1480712890625 -0.17425537109375 0.1501953125000029 +0.493 -0.143890380859375 -0.169342041015625 0.1501953125000029 +0.493125000000000052 -0.143890380859375 -0.169342041015625 0.1501953125000029 +0.49325 -0.139678955078125 -0.164398193359375 0.1501953125000029 +0.493375 -0.139678955078125 -0.164398193359375 0.1501953125000029 +0.4935 -0.135498046875 -0.159454345703125 0.1501953125000029 +0.493625000000000052 -0.131256103515625 -0.15447998046875 0.1501953125000029 +0.49375 -0.131256103515625 -0.15447998046875 0.1501953125000029 +0.493875 -0.12701416015625 -0.14947509765625 0.1501953125000029 +0.494 -0.12701416015625 -0.14947509765625 0.1501953125000029 +0.494125000000000064 -0.122772216796875 -0.14447021484375 0.1501953125000029 +0.49425 -0.118499755859375 -0.13946533203125 0.1501953125000029 +0.494375 -0.118499755859375 -0.13946533203125 0.1501953125000029 +0.4945 -0.114227294921875 -0.134429931640625 0.1501953125000029 +0.494625000000000064 -0.114227294921875 -0.134429931640625 0.1501953125000029 +0.49475 -0.10992431640625 -0.129364013671875 0.1501953125000029 +0.494875 -0.105621337890625 -0.124298095703125 0.1501953125000029 +0.495 -0.105621337890625 -0.124298095703125 0.1501953125000029 +0.495125000000000064 -0.101318359375 -0.119232177734375 0.1501953125000029 +0.49525 -0.101318359375 -0.119232177734375 0.1501953125000029 +0.495375 -0.09698486328125 -0.1141357421875 0.1501953125000029 +0.4955 -0.0926513671875 -0.109039306640625 0.1501953125000029 +0.495625000000000064 -0.0926513671875 -0.109039306640625 0.1501953125000029 +0.49575 -0.08831787109375 -0.103912353515625 0.1501953125000029 +0.495875 -0.08831787109375 -0.103912353515625 0.1501953125000029 +0.496 -0.083953857421875 -0.098785400390625 0.1501953125000029 +0.496125 -0.07958984375 -0.093658447265625 0.1501953125000029 +0.49625 -0.07958984375 -0.093658447265625 0.1501953125000029 +0.496375 -0.0751953125 -0.0885009765625 0.1501953125000029 +0.4965 -0.0751953125 -0.0885009765625 0.1501953125000029 +0.496625 -0.070831298828125 -0.083343505859375 0.1501953125000029 +0.49675 -0.066436767578125 -0.07818603515625 0.1501953125000029 +0.496875 -0.066436767578125 -0.07818603515625 0.1501953125000029 +0.497 -0.062042236328125 -0.072998046875 0.1501953125000029 +0.497125 -0.062042236328125 -0.072998046875 0.1501953125000029 +0.49725 -0.0576171875 -0.06781005859375 0.1501953125000029 +0.497375 -0.05322265625 -0.0626220703125 0.1501953125000029 +0.4975 -0.05322265625 -0.0626220703125 0.1501953125000029 +0.497625 -0.048797607421875 -0.05743408203125 0.1501953125000029 +0.49775 -0.048797607421875 -0.05743408203125 0.1501953125000029 +0.497875 -0.044403076171875 -0.05224609375 0.1501953125000029 +0.498 -0.039947509765625 -0.047027587890625 0.1501953125000029 +0.498125 -0.039947509765625 -0.047027587890625 0.1501953125000029 +0.49825 -0.0355224609375 -0.04180908203125 0.1501953125000029 +0.498375 -0.0355224609375 -0.04180908203125 0.1501953125000029 +0.4985 -0.031097412109375 -0.036590576171875 0.1501953125000029 +0.498625 -0.026641845703125 -0.0313720703125 0.1501953125000029 +0.49875 -0.026641845703125 -0.0313720703125 0.1501953125000029 +0.498875 -0.022216796875 -0.026123046875 0.1501953125000029 +0.499 -0.022216796875 -0.026123046875 0.1501953125000029 +0.499125 -0.01776123046875 -0.020904541015625 0.1501953125000029 +0.49925 -0.013336181640625 -0.01568603515625 0.1501953125000029 +0.499375 -0.013336181640625 -0.01568603515625 0.1501953125000029 +0.4995 -0.008880615234375 -0.01043701171875 0.1501953125000029 +0.499625 -0.008880615234375 -0.01043701171875 0.1501953125000029 +0.49975 -0.004425048828125 -0.005218505859375 0.1501953125000029 +0.499875 0.0 0.0 0.1501953125000029 +0.5 0.0 0.0 0.1501953125000029 +0.500125 0.004425048828125 0.005218505859375 0.1501953125000029 +0.50025 0.004425048828125 0.005218505859375 0.1501953125000029 +0.500375 0.008880615234375 0.01043701171875 0.1501953125000029 +0.5005000000000001 0.013336181640625 0.01568603515625 0.1501953125000029 +0.500625 0.013336181640625 0.01568603515625 0.1501953125000029 +0.50075 0.01776123046875 0.020904541015625 0.1501953125000029 +0.500875 0.01776123046875 0.020904541015625 0.1501953125000029 +0.501 0.022216796875 0.026123046875 0.1501953125000029 +0.501125 0.026641845703125 0.0313720703125 0.1501953125000029 +0.50125 0.026641845703125 0.0313720703125 0.1501953125000029 +0.501375 0.031097412109375 0.036590576171875 0.1501953125000029 +0.5015000000000001 0.031097412109375 0.036590576171875 0.1501953125000029 +0.501625 0.0355224609375 0.04180908203125 0.1501953125000029 +0.50175 0.039947509765625 0.047027587890625 0.1501953125000029 +0.501875 0.039947509765625 0.047027587890625 0.1501953125000029 +0.502 0.044403076171875 0.05224609375 0.1501953125000029 +0.502125 0.044403076171875 0.05224609375 0.1501953125000029 +0.50225 0.048797607421875 0.05743408203125 0.1501953125000029 +0.502375 0.05322265625 0.0626220703125 0.1501953125000029 +0.5025000000000001 0.05322265625 0.0626220703125 0.1501953125000029 +0.502625 0.0576171875 0.06781005859375 0.1501953125000029 +0.50275 0.0576171875 0.06781005859375 0.1501953125000029 +0.502875 0.062042236328125 0.072998046875 0.1501953125000029 +0.503 0.066436767578125 0.07818603515625 0.1501953125000029 +0.503125 0.066436767578125 0.07818603515625 0.1501953125000029 +0.50325 0.070831298828125 0.083343505859375 0.1501953125000029 +0.503375 0.070831298828125 0.083343505859375 0.1501953125000029 +0.5035000000000001 0.0751953125 0.0885009765625 0.1501953125000029 +0.503625 0.07958984375 0.093658447265625 0.1501953125000029 +0.50375 0.07958984375 0.093658447265625 0.1501953125000029 +0.503875 0.083953857421875 0.098785400390625 0.1501953125000029 +0.504 0.083953857421875 0.098785400390625 0.1501953125000029 +0.504125 0.08831787109375 0.103912353515625 0.1501953125000029 +0.50425 0.0926513671875 0.109039306640625 0.1501953125000029 +0.504375 0.0926513671875 0.109039306640625 0.1501953125000029 +0.5045000000000001 0.09698486328125 0.1141357421875 0.1501953125000029 +0.504625 0.09698486328125 0.1141357421875 0.1501953125000029 +0.50475 0.101318359375 0.119232177734375 0.1501953125000029 +0.504875 0.105621337890625 0.124298095703125 0.1501953125000029 +0.505 0.105621337890625 0.124298095703125 0.1501953125000029 +0.505125 0.10992431640625 0.129364013671875 0.1501953125000029 +0.50525 0.10992431640625 0.129364013671875 0.1501953125000029 +0.505375 0.114227294921875 0.134429931640625 0.1501953125000029 +0.5055000000000001 0.118499755859375 0.13946533203125 0.1501953125000029 +0.505625 0.118499755859375 0.13946533203125 0.1501953125000029 +0.50575 0.122772216796875 0.14447021484375 0.1501953125000029 +0.505875 0.122772216796875 0.14447021484375 0.1501953125000029 +0.506 0.12701416015625 0.14947509765625 0.1501953125000029 +0.506125 0.131256103515625 0.15447998046875 0.1501953125000029 +0.50625 0.131256103515625 0.15447998046875 0.1501953125000029 +0.506375 0.135498046875 0.159454345703125 0.1501953125000029 +0.5065000000000001 0.135498046875 0.159454345703125 0.1501953125000029 +0.506625 0.139678955078125 0.164398193359375 0.1501953125000029 +0.50675 0.143890380859375 0.169342041015625 0.1501953125000029 +0.506875 0.143890380859375 0.169342041015625 0.1501953125000029 +0.507 0.1480712890625 0.17425537109375 0.1501953125000029 +0.507125 0.1480712890625 0.17425537109375 0.1501953125000029 +0.50725 0.1522216796875 0.17913818359375 0.1501953125000029 +0.507375 0.1563720703125 0.18402099609375 0.1501953125000029 +0.5075000000000001 0.1563720703125 0.18402099609375 0.1501953125000029 +0.507625 0.160491943359375 0.188873291015625 0.1501953125000029 +0.50775 0.160491943359375 0.188873291015625 0.1501953125000029 +0.507875 0.16461181640625 0.1937255859375 0.1501953125000029 +0.508 0.168701171875 0.19854736328125 0.1501953125000029 +0.508125 0.168701171875 0.19854736328125 0.1501953125000029 +0.50825 0.172760009765625 0.203338623046875 0.1501953125000029 +0.508375 0.172760009765625 0.203338623046875 0.1501953125000029 +0.5085000000000001 0.17681884765625 0.208099365234375 0.1501953125000029 +0.508625 0.180877685546875 0.212860107421875 0.1501953125000029 +0.50875 0.180877685546875 0.212860107421875 0.1501953125000029 +0.508875 0.18487548828125 0.21759033203125 0.1501953125000029 +0.509 0.18487548828125 0.21759033203125 0.1501953125000029 +0.509125 0.188873291015625 0.2222900390625 0.1501953125000029 +0.50925 0.192840576171875 0.226959228515625 0.1501953125000029 +0.509375 0.192840576171875 0.226959228515625 0.1501953125000029 +0.5095000000000001 0.196807861328125 0.231597900390625 0.1501953125000029 +0.509625 0.196807861328125 0.231597900390625 0.1501953125000029 +0.50975 0.20074462890625 0.236236572265625 0.1501953125000029 +0.509875 0.20465087890625 0.2408447265625 0.1501953125000029 +0.51 0.20465087890625 0.2408447265625 0.1501953125000029 +0.5101250000000001 0.208526611328125 0.24542236328125 0.1501953125000029 +0.51025 0.208526611328125 0.24542236328125 0.1501953125000029 +0.510375 0.21240234375 0.249969482421875 0.1501953125000029 +0.5105000000000001 0.21624755859375 0.25445556640625 0.1501953125000029 +0.510625 0.21624755859375 0.25445556640625 0.1501953125000029 +0.51075 0.220062255859375 0.25897216796875 0.1501953125000029 +0.510875 0.220062255859375 0.25897216796875 0.1501953125000029 +0.511 0.223846435546875 0.263427734375 0.1501953125000029 +0.5111250000000001 0.227630615234375 0.267852783203125 0.1501953125000029 +0.51125 0.227630615234375 0.267852783203125 0.1501953125000029 +0.511375 0.231353759765625 0.27227783203125 0.1501953125000029 +0.5115000000000001 0.231353759765625 0.27227783203125 0.1501953125000029 +0.511625 0.235076904296875 0.276641845703125 0.1501953125000029 +0.51175 0.23876953125 0.280975341796875 0.1501953125000029 +0.511875 0.23876953125 0.280975341796875 0.1501953125000029 +0.512 0.135467529296875 0.285308837890625 0.5251342773437525 +0.5121250000000001 0.135467529296875 0.285308837890625 0.5251342773437525 +0.51225 0.137481689453125 0.289581298828125 0.5251342773437525 +0.512375 0.139495849609375 0.2938232421875 0.5251342773437525 +0.5125 0.139495849609375 0.2938232421875 0.5251342773437525 +0.512625 0.141510009765625 0.298065185546875 0.5251342773437525 +0.51275 0.141510009765625 0.298065185546875 0.5251342773437525 +0.512875 0.14349365234375 0.30224609375 0.5251342773437525 +0.513 0.145477294921875 0.306396484375 0.5251342773437525 +0.5131250000000001 0.145477294921875 0.306396484375 0.5251342773437525 +0.51325 0.147430419921875 0.310516357421875 0.5251342773437525 +0.513375 0.147430419921875 0.310516357421875 0.5251342773437525 +0.5135 0.14935302734375 0.314605712890625 0.5251342773437525 +0.513625 0.151275634765625 0.31866455078125 0.5251342773437525 +0.51375 0.151275634765625 0.31866455078125 0.5251342773437525 +0.513875 0.1531982421875 0.322662353515625 0.5251342773437525 +0.514 0.1531982421875 0.322662353515625 0.5251342773437525 +0.5141250000000001 0.15509033203125 0.32666015625 0.5251342773437525 +0.51425 0.156951904296875 0.330596923828125 0.5251342773437525 +0.514375 0.156951904296875 0.330596923828125 0.5251342773437525 +0.5145 0.1588134765625 0.334503173828125 0.5251342773437525 +0.514625 0.1588134765625 0.334503173828125 0.5251342773437525 +0.51475 0.16064453125 0.33837890625 0.5251342773437525 +0.514875 0.1624755859375 0.34222412109375 0.5251342773437525 +0.515 0.1624755859375 0.34222412109375 0.5251342773437525 +0.5151250000000001 0.164276123046875 0.34600830078125 0.5251342773437525 +0.51525 0.164276123046875 0.34600830078125 0.5251342773437525 +0.515375 0.166046142578125 0.349761962890625 0.5251342773437525 +0.5155 0.167816162109375 0.353485107421875 0.5251342773437525 +0.515625 0.167816162109375 0.353485107421875 0.5251342773437525 +0.51575 0.169586181640625 0.357177734375 0.5251342773437525 +0.515875 0.169586181640625 0.357177734375 0.5251342773437525 +0.516 0.171295166015625 0.360809326171875 0.5251342773437525 +0.5161250000000001 0.17303466796875 0.36444091796875 0.5251342773437525 +0.51625 0.17303466796875 0.36444091796875 0.5251342773437525 +0.516375 0.174713134765625 0.36798095703125 0.5251342773437525 +0.5165 0.174713134765625 0.36798095703125 0.5251342773437525 +0.516625 0.1763916015625 0.37152099609375 0.5251342773437525 +0.51675 0.17803955078125 0.375 0.5251342773437525 +0.516875 0.17803955078125 0.375 0.5251342773437525 +0.517 0.179656982421875 0.378448486328125 0.5251342773437525 +0.5171250000000001 0.179656982421875 0.378448486328125 0.5251342773437525 +0.51725 0.1812744140625 0.3818359375 0.5251342773437525 +0.517375 0.182891845703125 0.38519287109375 0.5251342773437525 +0.5175 0.182891845703125 0.38519287109375 0.5251342773437525 +0.517625 0.1844482421875 0.388519287109375 0.5251342773437525 +0.51775 0.1844482421875 0.388519287109375 0.5251342773437525 +0.517875 0.186004638671875 0.39178466796875 0.5251342773437525 +0.518 0.18756103515625 0.39501953125 0.5251342773437525 +0.5181250000000001 0.18756103515625 0.39501953125 0.5251342773437525 +0.51825 0.189056396484375 0.398193359375 0.5251342773437525 +0.518375 0.189056396484375 0.398193359375 0.5251342773437525 +0.5185 0.1905517578125 0.401336669921875 0.5251342773437525 +0.518625 0.1920166015625 0.404449462890625 0.5251342773437525 +0.51875 0.1920166015625 0.404449462890625 0.5251342773437525 +0.518875 0.1934814453125 0.407501220703125 0.5251342773437525 +0.519 0.1934814453125 0.407501220703125 0.5251342773437525 +0.5191250000000001 0.19488525390625 0.4105224609375 0.5251342773437525 +0.51925 0.196319580078125 0.413482666015625 0.5251342773437525 +0.519375 0.196319580078125 0.413482666015625 0.5251342773437525 +0.5195 0.19769287109375 0.416412353515625 0.5251342773437525 +0.519625 0.19769287109375 0.416412353515625 0.5251342773437525 +0.51975 0.199066162109375 0.419281005859375 0.5251342773437525 +0.519875 0.200408935546875 0.422119140625 0.5251342773437525 +0.52 0.200408935546875 0.422119140625 0.5251342773437525 +0.5201250000000001 0.20172119140625 0.424896240234375 0.5251342773437525 +0.52025 0.20172119140625 0.424896240234375 0.5251342773437525 +0.520375 0.203033447265625 0.4276123046875 0.5251342773437525 +0.5205 0.20428466796875 0.4302978515625 0.5251342773437525 +0.520625 0.20428466796875 0.4302978515625 0.5251342773437525 +0.52075 0.20556640625 0.432952880859375 0.5251342773437525 +0.520875 0.20556640625 0.432952880859375 0.5251342773437525 +0.521 0.206787109375 0.435546875 0.5251342773437525 +0.5211250000000001 0.207977294921875 0.438079833984375 0.5251342773437525 +0.52125 0.207977294921875 0.438079833984375 0.5251342773437525 +0.521375 0.20916748046875 0.440582275390625 0.5251342773437525 +0.5215 0.20916748046875 0.440582275390625 0.5251342773437525 +0.521625 0.210357666015625 0.44305419921875 0.5251342773437525 +0.52175 0.21148681640625 0.4454345703125 0.5251342773437525 +0.521875 0.21148681640625 0.4454345703125 0.5251342773437525 +0.522 0.21258544921875 0.447784423828125 0.5251342773437525 +0.5221250000000001 0.21258544921875 0.447784423828125 0.5251342773437525 +0.52225 0.21368408203125 0.450103759765625 0.5251342773437525 +0.522375 0.214752197265625 0.452362060546875 0.5251342773437525 +0.5225 0.214752197265625 0.452362060546875 0.5251342773437525 +0.522625 0.2158203125 0.454559326171875 0.5251342773437525 +0.52275 0.2158203125 0.454559326171875 0.5251342773437525 +0.522875 0.216827392578125 0.45672607421875 0.5251342773437525 +0.523 0.21783447265625 0.458831787109375 0.5251342773437525 +0.5231250000000001 0.21783447265625 0.458831787109375 0.5251342773437525 +0.52325 0.21881103515625 0.46087646484375 0.5251342773437525 +0.523375 0.21881103515625 0.46087646484375 0.5251342773437525 +0.5235 0.219757080078125 0.462890625 0.5251342773437525 +0.523625 0.220672607421875 0.464813232421875 0.5251342773437525 +0.52375 0.220672607421875 0.464813232421875 0.5251342773437525 +0.523875 0.221588134765625 0.46673583984375 0.5251342773437525 +0.524 0.221588134765625 0.46673583984375 0.5251342773437525 +0.5241250000000001 0.22247314453125 0.46856689453125 0.5251342773437525 +0.52425 0.22332763671875 0.470367431640625 0.5251342773437525 +0.524375 0.22332763671875 0.470367431640625 0.5251342773437525 +0.5245 0.224151611328125 0.472137451171875 0.5251342773437525 +0.524625 0.224151611328125 0.472137451171875 0.5251342773437525 +0.52475 0.224945068359375 0.47381591796875 0.5251342773437525 +0.524875 0.225738525390625 0.4754638671875 0.5251342773437525 +0.525 0.225738525390625 0.4754638671875 0.5251342773437525 +0.5251250000000001 0.22650146484375 0.47705078125 0.5251342773437525 +0.52525 0.22650146484375 0.47705078125 0.5251342773437525 +0.525375 0.22723388671875 0.478607177734375 0.5251342773437525 +0.5255 0.227935791015625 0.480072021484375 0.5251342773437525 +0.525625 0.227935791015625 0.480072021484375 0.5251342773437525 +0.5257500000000001 0.228607177734375 0.48150634765625 0.5251342773437525 +0.525875 0.228607177734375 0.48150634765625 0.5251342773437525 +0.526 0.229278564453125 0.48291015625 0.5251342773437525 +0.5261250000000001 0.229888916015625 0.484222412109375 0.5251342773437525 +0.52625 0.229888916015625 0.484222412109375 0.5251342773437525 +0.526375 0.230499267578125 0.485504150390625 0.5251342773437525 +0.5265 0.230499267578125 0.485504150390625 0.5251342773437525 +0.526625 0.2310791015625 0.486724853515625 0.5251342773437525 +0.5267500000000001 0.23162841796875 0.487884521484375 0.5251342773437525 +0.526875 0.23162841796875 0.487884521484375 0.5251342773437525 +0.527 0.232177734375 0.489013671875 0.5251342773437525 +0.5271250000000001 0.232177734375 0.489013671875 0.5251342773437525 +0.52725 0.232666015625 0.490081787109375 0.5251342773437525 +0.527375 0.233154296875 0.4910888671875 0.5251342773437525 +0.5275 0.233154296875 0.4910888671875 0.5251342773437525 +0.527625 0.233612060546875 0.492034912109375 0.5251342773437525 +0.5277500000000001 0.233612060546875 0.492034912109375 0.5251342773437525 +0.527875 0.234039306640625 0.492950439453125 0.5251342773437525 +0.528 0.23443603515625 0.4937744140625 0.5251342773437525 +0.528125 0.23443603515625 0.4937744140625 0.5251342773437525 +0.52825 0.23480224609375 0.49456787109375 0.5251342773437525 +0.528375 0.23480224609375 0.49456787109375 0.5251342773437525 +0.5285 0.23516845703125 0.49530029296875 0.5251342773437525 +0.528625 0.2354736328125 0.496002197265625 0.5251342773437525 +0.5287500000000001 0.2354736328125 0.496002197265625 0.5251342773437525 +0.528875 0.23577880859375 0.496612548828125 0.5251342773437525 +0.529 0.23577880859375 0.496612548828125 0.5251342773437525 +0.529125 0.236053466796875 0.4971923828125 0.5251342773437525 +0.52925 0.236297607421875 0.497711181640625 0.5251342773437525 +0.529375 0.236297607421875 0.497711181640625 0.5251342773437525 +0.5295 0.23651123046875 0.498199462890625 0.5251342773437525 +0.529625 0.23651123046875 0.498199462890625 0.5251342773437525 +0.5297500000000001 0.236724853515625 0.49859619140625 0.5251342773437525 +0.529875 0.23687744140625 0.49896240234375 0.5251342773437525 +0.53 0.23687744140625 0.49896240234375 0.5251342773437525 +0.530125 0.237030029296875 0.499267578125 0.5251342773437525 +0.53025 0.237030029296875 0.499267578125 0.5251342773437525 +0.530375 0.237152099609375 0.49951171875 0.5251342773437525 +0.5305 0.23724365234375 0.49969482421875 0.5251342773437525 +0.530625 0.23724365234375 0.49969482421875 0.5251342773437525 +0.5307500000000001 0.2373046875 0.49981689453125 0.5251342773437525 +0.530875 0.2373046875 0.49981689453125 0.5251342773437525 +0.531 0.237335205078125 0.499908447265625 0.5251342773437525 +0.531125 0.23736572265625 0.49993896484375 0.5251342773437525 +0.53125 0.23736572265625 0.49993896484375 0.5251342773437525 +0.531375 0.237335205078125 0.499908447265625 0.5251342773437525 +0.5315 0.237335205078125 0.499908447265625 0.5251342773437525 +0.531625 0.2373046875 0.49981689453125 0.5251342773437525 +0.5317500000000001 0.23724365234375 0.49969482421875 0.5251342773437525 +0.531875 0.23724365234375 0.49969482421875 0.5251342773437525 +0.532 0.237152099609375 0.49951171875 0.5251342773437525 +0.532125 0.237152099609375 0.49951171875 0.5251342773437525 +0.53225 0.237030029296875 0.499267578125 0.5251342773437525 +0.532375 0.23687744140625 0.49896240234375 0.5251342773437525 +0.5325 0.23687744140625 0.49896240234375 0.5251342773437525 +0.532625 0.236724853515625 0.49859619140625 0.5251342773437525 +0.5327500000000001 0.236724853515625 0.49859619140625 0.5251342773437525 +0.532875 0.23651123046875 0.498199462890625 0.5251342773437525 +0.533 0.236297607421875 0.497711181640625 0.5251342773437525 +0.533125 0.236297607421875 0.497711181640625 0.5251342773437525 +0.53325 0.236053466796875 0.4971923828125 0.5251342773437525 +0.533375 0.236053466796875 0.4971923828125 0.5251342773437525 +0.5335 0.23577880859375 0.496612548828125 0.5251342773437525 +0.533625 0.2354736328125 0.496002197265625 0.5251342773437525 +0.5337500000000001 0.2354736328125 0.496002197265625 0.5251342773437525 +0.533875 0.23516845703125 0.49530029296875 0.5251342773437525 +0.534 0.23516845703125 0.49530029296875 0.5251342773437525 +0.534125 0.23480224609375 0.49456787109375 0.5251342773437525 +0.53425 0.23443603515625 0.4937744140625 0.5251342773437525 +0.534375 0.23443603515625 0.4937744140625 0.5251342773437525 +0.5345 0.234039306640625 0.492950439453125 0.5251342773437525 +0.534625 0.234039306640625 0.492950439453125 0.5251342773437525 +0.5347500000000001 0.233612060546875 0.492034912109375 0.5251342773437525 +0.534875 0.233154296875 0.4910888671875 0.5251342773437525 +0.535 0.233154296875 0.4910888671875 0.5251342773437525 +0.535125 0.232666015625 0.490081787109375 0.5251342773437525 +0.53525 0.232666015625 0.490081787109375 0.5251342773437525 +0.535375 0.232177734375 0.489013671875 0.5251342773437525 +0.5355 0.23162841796875 0.487884521484375 0.5251342773437525 +0.535625 0.23162841796875 0.487884521484375 0.5251342773437525 +0.5357500000000001 0.2310791015625 0.486724853515625 0.5251342773437525 +0.535875 0.2310791015625 0.486724853515625 0.5251342773437525 +0.536 0.230499267578125 0.485504150390625 0.5251342773437525 +0.536125 0.229888916015625 0.484222412109375 0.5251342773437525 +0.53625 0.229888916015625 0.484222412109375 0.5251342773437525 +0.536375 0.229278564453125 0.48291015625 0.5251342773437525 +0.5365 0.229278564453125 0.48291015625 0.5251342773437525 +0.536625 0.228607177734375 0.48150634765625 0.5251342773437525 +0.5367500000000001 0.227935791015625 0.480072021484375 0.5251342773437525 +0.536875 0.227935791015625 0.480072021484375 0.5251342773437525 +0.537 0.22723388671875 0.478607177734375 0.5251342773437525 +0.537125 0.22723388671875 0.478607177734375 0.5251342773437525 +0.53725 0.22650146484375 0.47705078125 0.5251342773437525 +0.537375 0.225738525390625 0.4754638671875 0.5251342773437525 +0.5375 0.225738525390625 0.4754638671875 0.5251342773437525 +0.537625 0.224945068359375 0.47381591796875 0.5251342773437525 +0.5377500000000001 0.224945068359375 0.47381591796875 0.5251342773437525 +0.537875 0.224151611328125 0.472137451171875 0.5251342773437525 +0.538 0.22332763671875 0.470367431640625 0.5251342773437525 +0.538125 0.22332763671875 0.470367431640625 0.5251342773437525 +0.53825 0.22247314453125 0.46856689453125 0.5251342773437525 +0.538375 0.22247314453125 0.46856689453125 0.5251342773437525 +0.5385 0.221588134765625 0.46673583984375 0.5251342773437525 +0.538625 0.220672607421875 0.464813232421875 0.5251342773437525 +0.5387500000000001 0.220672607421875 0.464813232421875 0.5251342773437525 +0.538875 0.219757080078125 0.462890625 0.5251342773437525 +0.539 0.219757080078125 0.462890625 0.5251342773437525 +0.539125 0.21881103515625 0.46087646484375 0.5251342773437525 +0.53925 0.21783447265625 0.458831787109375 0.5251342773437525 +0.539375 0.21783447265625 0.458831787109375 0.5251342773437525 +0.5395 0.216827392578125 0.45672607421875 0.5251342773437525 +0.539625 0.216827392578125 0.45672607421875 0.5251342773437525 +0.5397500000000001 0.2158203125 0.454559326171875 0.5251342773437525 +0.539875 0.214752197265625 0.452362060546875 0.5251342773437525 +0.54 0.214752197265625 0.452362060546875 0.5251342773437525 +0.540125 0.21368408203125 0.450103759765625 0.5251342773437525 +0.54025 0.21368408203125 0.450103759765625 0.5251342773437525 +0.540375 0.21258544921875 0.447784423828125 0.5251342773437525 +0.5405 0.21148681640625 0.4454345703125 0.5251342773437525 +0.540625 0.21148681640625 0.4454345703125 0.5251342773437525 +0.5407500000000001 0.210357666015625 0.44305419921875 0.5251342773437525 +0.540875 0.210357666015625 0.44305419921875 0.5251342773437525 +0.541 0.20916748046875 0.440582275390625 0.5251342773437525 +0.541125 0.207977294921875 0.438079833984375 0.5251342773437525 +0.54125 0.207977294921875 0.438079833984375 0.5251342773437525 +0.5413750000000001 0.206787109375 0.435546875 0.5251342773437525 +0.5415 0.206787109375 0.435546875 0.5251342773437525 +0.541625 0.20556640625 0.432952880859375 0.5251342773437525 +0.5417500000000001 0.20428466796875 0.4302978515625 0.5251342773437525 +0.541875 0.20428466796875 0.4302978515625 0.5251342773437525 +0.542 0.203033447265625 0.4276123046875 0.5251342773437525 +0.542125 0.203033447265625 0.4276123046875 0.5251342773437525 +0.54225 0.20172119140625 0.424896240234375 0.5251342773437525 +0.5423750000000001 0.200408935546875 0.422119140625 0.5251342773437525 +0.5425 0.200408935546875 0.422119140625 0.5251342773437525 +0.542625 0.199066162109375 0.419281005859375 0.5251342773437525 +0.5427500000000001 0.199066162109375 0.419281005859375 0.5251342773437525 +0.542875 0.19769287109375 0.416412353515625 0.5251342773437525 +0.543 0.196319580078125 0.413482666015625 0.5251342773437525 +0.543125 0.196319580078125 0.413482666015625 0.5251342773437525 +0.54325 0.19488525390625 0.4105224609375 0.5251342773437525 +0.5433750000000001 0.19488525390625 0.4105224609375 0.5251342773437525 +0.5435 0.1934814453125 0.407501220703125 0.5251342773437525 +0.543625 0.1920166015625 0.404449462890625 0.5251342773437525 +0.5437500000000001 0.1920166015625 0.404449462890625 0.5251342773437525 +0.543875 0.1905517578125 0.401336669921875 0.5251342773437525 +0.544 0.073699951171875 0.401336669921875 0.8162963867187517 +0.544125 0.0731201171875 0.398193359375 0.8162963867187517 +0.54425 0.072540283203125 0.39501953125 0.8162963867187517 +0.5443750000000001 0.072540283203125 0.39501953125 0.8162963867187517 +0.5445 0.071929931640625 0.39178466796875 0.8162963867187517 +0.544625 0.071929931640625 0.39178466796875 0.8162963867187517 +0.54475 0.071319580078125 0.388519287109375 0.8162963867187517 +0.544875 0.070709228515625 0.38519287109375 0.8162963867187517 +0.545 0.070709228515625 0.38519287109375 0.8162963867187517 +0.545125 0.070098876953125 0.3818359375 0.8162963867187517 +0.54525 0.070098876953125 0.3818359375 0.8162963867187517 +0.5453750000000001 0.069488525390625 0.378448486328125 0.8162963867187517 +0.5455 0.06884765625 0.375 0.8162963867187517 +0.545625 0.06884765625 0.375 0.8162963867187517 +0.54575 0.068206787109375 0.37152099609375 0.8162963867187517 +0.545875 0.068206787109375 0.37152099609375 0.8162963867187517 +0.546 0.06756591796875 0.36798095703125 0.8162963867187517 +0.546125 0.066925048828125 0.36444091796875 0.8162963867187517 +0.54625 0.066925048828125 0.36444091796875 0.8162963867187517 +0.5463750000000001 0.066253662109375 0.360809326171875 0.8162963867187517 +0.5465 0.066253662109375 0.360809326171875 0.8162963867187517 +0.546625 0.065582275390625 0.357177734375 0.8162963867187517 +0.54675 0.064910888671875 0.353485107421875 0.8162963867187517 +0.546875 0.064910888671875 0.353485107421875 0.8162963867187517 +0.547 0.064208984375 0.349761962890625 0.8162963867187517 +0.547125 0.064208984375 0.349761962890625 0.8162963867187517 +0.54725 0.06353759765625 0.34600830078125 0.8162963867187517 +0.5473750000000001 0.062835693359375 0.34222412109375 0.8162963867187517 +0.5475 0.062835693359375 0.34222412109375 0.8162963867187517 +0.547625 0.0621337890625 0.33837890625 0.8162963867187517 +0.54775 0.0621337890625 0.33837890625 0.8162963867187517 +0.547875 0.0614013671875 0.334503173828125 0.8162963867187517 +0.548 0.060699462890625 0.330596923828125 0.8162963867187517 +0.548125 0.060699462890625 0.330596923828125 0.8162963867187517 +0.54825 0.059967041015625 0.32666015625 0.8162963867187517 +0.5483750000000001 0.059967041015625 0.32666015625 0.8162963867187517 +0.5485 0.059234619140625 0.322662353515625 0.8162963867187517 +0.548625 0.058502197265625 0.31866455078125 0.8162963867187517 +0.54875 0.058502197265625 0.31866455078125 0.8162963867187517 +0.548875 0.057769775390625 0.314605712890625 0.8162963867187517 +0.549 0.057769775390625 0.314605712890625 0.8162963867187517 +0.549125 0.0570068359375 0.310516357421875 0.8162963867187517 +0.54925 0.056243896484375 0.306396484375 0.8162963867187517 +0.5493750000000001 0.056243896484375 0.306396484375 0.8162963867187517 +0.5495 0.05548095703125 0.30224609375 0.8162963867187517 +0.549625 0.05548095703125 0.30224609375 0.8162963867187517 +0.54975 0.054718017578125 0.298065185546875 0.8162963867187517 +0.549875 0.053955078125 0.2938232421875 0.8162963867187517 +0.55 0.053955078125 0.2938232421875 0.8162963867187517 +0.550125 0.05316162109375 0.289581298828125 0.8162963867187517 +0.55025 0.05316162109375 0.289581298828125 0.8162963867187517 +0.5503750000000001 0.0523681640625 0.285308837890625 0.8162963867187517 +0.5505 0.05157470703125 0.280975341796875 0.8162963867187517 +0.550625 0.05157470703125 0.280975341796875 0.8162963867187517 +0.55075 0.05078125 0.276641845703125 0.8162963867187517 +0.550875 0.05078125 0.276641845703125 0.8162963867187517 +0.551 0.04998779296875 0.27227783203125 0.8162963867187517 +0.551125 0.049163818359375 0.267852783203125 0.8162963867187517 +0.55125 0.049163818359375 0.267852783203125 0.8162963867187517 +0.5513750000000001 0.048370361328125 0.263427734375 0.8162963867187517 +0.5515 0.048370361328125 0.263427734375 0.8162963867187517 +0.551625 0.04754638671875 0.25897216796875 0.8162963867187517 +0.55175 0.046722412109375 0.25445556640625 0.8162963867187517 +0.551875 0.046722412109375 0.25445556640625 0.8162963867187517 +0.552 0.0458984375 0.249969482421875 0.8162963867187517 +0.552125 0.0458984375 0.249969482421875 0.8162963867187517 +0.55225 0.0450439453125 0.24542236328125 0.8162963867187517 +0.5523750000000001 0.044219970703125 0.2408447265625 0.8162963867187517 +0.5525 0.044219970703125 0.2408447265625 0.8162963867187517 +0.552625 0.043365478515625 0.236236572265625 0.8162963867187517 +0.55275 0.043365478515625 0.236236572265625 0.8162963867187517 +0.552875 0.042510986328125 0.231597900390625 0.8162963867187517 +0.553 0.041656494140625 0.226959228515625 0.8162963867187517 +0.553125 0.041656494140625 0.226959228515625 0.8162963867187517 +0.55325 0.040802001953125 0.2222900390625 0.8162963867187517 +0.5533750000000001 0.040802001953125 0.2222900390625 0.8162963867187517 +0.5535 0.039947509765625 0.21759033203125 0.8162963867187517 +0.553625 0.0390625 0.212860107421875 0.8162963867187517 +0.55375 0.0390625 0.212860107421875 0.8162963867187517 +0.553875 0.0382080078125 0.208099365234375 0.8162963867187517 +0.554 0.0382080078125 0.208099365234375 0.8162963867187517 +0.554125 0.037322998046875 0.203338623046875 0.8162963867187517 +0.55425 0.03643798828125 0.19854736328125 0.8162963867187517 +0.5543750000000001 0.03643798828125 0.19854736328125 0.8162963867187517 +0.5545 0.035552978515625 0.1937255859375 0.8162963867187517 +0.554625 0.035552978515625 0.1937255859375 0.8162963867187517 +0.55475 0.03466796875 0.188873291015625 0.8162963867187517 +0.554875 0.033782958984375 0.18402099609375 0.8162963867187517 +0.555 0.033782958984375 0.18402099609375 0.8162963867187517 +0.555125 0.03289794921875 0.17913818359375 0.8162963867187517 +0.55525 0.03289794921875 0.17913818359375 0.8162963867187517 +0.5553750000000001 0.031982421875 0.17425537109375 0.8162963867187517 +0.5555 0.031097412109375 0.169342041015625 0.8162963867187517 +0.555625 0.031097412109375 0.169342041015625 0.8162963867187517 +0.55575 0.030181884765625 0.164398193359375 0.8162963867187517 +0.555875 0.030181884765625 0.164398193359375 0.8162963867187517 +0.556 0.029266357421875 0.159454345703125 0.8162963867187517 +0.556125 0.028350830078125 0.15447998046875 0.8162963867187517 +0.55625 0.028350830078125 0.15447998046875 0.8162963867187517 +0.5563750000000001 0.027435302734375 0.14947509765625 0.8162963867187517 +0.5565 0.027435302734375 0.14947509765625 0.8162963867187517 +0.556625 0.026519775390625 0.14447021484375 0.8162963867187517 +0.55675 0.025604248046875 0.13946533203125 0.8162963867187517 +0.556875 0.025604248046875 0.13946533203125 0.8162963867187517 +0.5570000000000001 0.024658203125 0.134429931640625 0.8162963867187517 +0.557125 0.024658203125 0.134429931640625 0.8162963867187517 +0.55725 0.02374267578125 0.129364013671875 0.8162963867187517 +0.5573750000000001 0.0228271484375 0.124298095703125 0.8162963867187517 +0.5575 0.0228271484375 0.124298095703125 0.8162963867187517 +0.557625 0.021881103515625 0.119232177734375 0.8162963867187517 +0.55775 0.021881103515625 0.119232177734375 0.8162963867187517 +0.557875 0.02093505859375 0.1141357421875 0.8162963867187517 +0.5580000000000001 0.02001953125 0.109039306640625 0.8162963867187517 +0.558125 0.02001953125 0.109039306640625 0.8162963867187517 +0.55825 0.019073486328125 0.103912353515625 0.8162963867187517 +0.5583750000000001 0.019073486328125 0.103912353515625 0.8162963867187517 +0.5585 0.01812744140625 0.098785400390625 0.8162963867187517 +0.558625 0.017181396484375 0.093658447265625 0.8162963867187517 +0.55875 0.017181396484375 0.093658447265625 0.8162963867187517 +0.558875 0.0162353515625 0.0885009765625 0.8162963867187517 +0.5590000000000001 0.0162353515625 0.0885009765625 0.8162963867187517 +0.559125 0.015289306640625 0.083343505859375 0.8162963867187517 +0.55925 0.01434326171875 0.07818603515625 0.8162963867187517 +0.5593750000000001 0.01434326171875 0.07818603515625 0.8162963867187517 +0.5595 0.013397216796875 0.072998046875 0.8162963867187517 +0.559625 0.013397216796875 0.072998046875 0.8162963867187517 +0.55975 0.012451171875 0.06781005859375 0.8162963867187517 +0.559875 0.011474609375 0.0626220703125 0.8162963867187517 +0.5600000000000001 0.011474609375 0.0626220703125 0.8162963867187517 +0.560125 0.010528564453125 0.05743408203125 0.8162963867187517 +0.56025 0.010528564453125 0.05743408203125 0.8162963867187517 +0.560375 0.00958251953125 0.05224609375 0.8162963867187517 +0.5605 0.008636474609375 0.047027587890625 0.8162963867187517 +0.560625 0.008636474609375 0.047027587890625 0.8162963867187517 +0.56075 0.007659912109375 0.04180908203125 0.8162963867187517 +0.560875 0.007659912109375 0.04180908203125 0.8162963867187517 +0.5610000000000001 0.0067138671875 0.036590576171875 0.8162963867187517 +0.561125 0.0057373046875 0.0313720703125 0.8162963867187517 +0.56125 0.0057373046875 0.0313720703125 0.8162963867187517 +0.561375 0.004791259765625 0.026123046875 0.8162963867187517 +0.5615 0.004791259765625 0.026123046875 0.8162963867187517 +0.561625 0.003814697265625 0.020904541015625 0.8162963867187517 +0.56175 0.00286865234375 0.01568603515625 0.8162963867187517 +0.561875 0.00286865234375 0.01568603515625 0.8162963867187517 +0.5620000000000001 0.00189208984375 0.01043701171875 0.8162963867187517 +0.562125 0.00189208984375 0.01043701171875 0.8162963867187517 +0.56225 0.000946044921875 0.005218505859375 0.8162963867187517 +0.562375 0.0 0.0 0.8162963867187517 +0.5625 0.0 0.0 0.8162963867187517 +0.562625 -0.000946044921875 -0.005218505859375 0.8162963867187517 +0.56275 -0.000946044921875 -0.005218505859375 0.8162963867187517 +0.562875 -0.00189208984375 -0.01043701171875 0.8162963867187517 +0.5630000000000001 -0.00286865234375 -0.01568603515625 0.8162963867187517 +0.563125 -0.00286865234375 -0.01568603515625 0.8162963867187517 +0.56325 -0.003814697265625 -0.020904541015625 0.8162963867187517 +0.563375 -0.003814697265625 -0.020904541015625 0.8162963867187517 +0.5635 -0.004791259765625 -0.026123046875 0.8162963867187517 +0.563625 -0.0057373046875 -0.0313720703125 0.8162963867187517 +0.56375 -0.0057373046875 -0.0313720703125 0.8162963867187517 +0.563875 -0.0067138671875 -0.036590576171875 0.8162963867187517 +0.5640000000000001 -0.0067138671875 -0.036590576171875 0.8162963867187517 +0.564125 -0.007659912109375 -0.04180908203125 0.8162963867187517 +0.56425 -0.008636474609375 -0.047027587890625 0.8162963867187517 +0.564375 -0.008636474609375 -0.047027587890625 0.8162963867187517 +0.5645 -0.00958251953125 -0.05224609375 0.8162963867187517 +0.564625 -0.00958251953125 -0.05224609375 0.8162963867187517 +0.56475 -0.010528564453125 -0.05743408203125 0.8162963867187517 +0.564875 -0.011474609375 -0.0626220703125 0.8162963867187517 +0.5650000000000001 -0.011474609375 -0.0626220703125 0.8162963867187517 +0.565125 -0.012451171875 -0.06781005859375 0.8162963867187517 +0.56525 -0.012451171875 -0.06781005859375 0.8162963867187517 +0.565375 -0.013397216796875 -0.072998046875 0.8162963867187517 +0.5655 -0.01434326171875 -0.07818603515625 0.8162963867187517 +0.565625 -0.01434326171875 -0.07818603515625 0.8162963867187517 +0.56575 -0.015289306640625 -0.083343505859375 0.8162963867187517 +0.565875 -0.015289306640625 -0.083343505859375 0.8162963867187517 +0.5660000000000001 -0.0162353515625 -0.0885009765625 0.8162963867187517 +0.566125 -0.017181396484375 -0.093658447265625 0.8162963867187517 +0.56625 -0.017181396484375 -0.093658447265625 0.8162963867187517 +0.566375 -0.01812744140625 -0.098785400390625 0.8162963867187517 +0.5665 -0.01812744140625 -0.098785400390625 0.8162963867187517 +0.566625 -0.019073486328125 -0.103912353515625 0.8162963867187517 +0.56675 -0.02001953125 -0.109039306640625 0.8162963867187517 +0.566875 -0.02001953125 -0.109039306640625 0.8162963867187517 +0.5670000000000001 -0.02093505859375 -0.1141357421875 0.8162963867187517 +0.567125 -0.02093505859375 -0.1141357421875 0.8162963867187517 +0.56725 -0.021881103515625 -0.119232177734375 0.8162963867187517 +0.567375 -0.0228271484375 -0.124298095703125 0.8162963867187517 +0.5675 -0.0228271484375 -0.124298095703125 0.8162963867187517 +0.567625 -0.02374267578125 -0.129364013671875 0.8162963867187517 +0.56775 -0.02374267578125 -0.129364013671875 0.8162963867187517 +0.567875 -0.024658203125 -0.134429931640625 0.8162963867187517 +0.5680000000000001 -0.025604248046875 -0.13946533203125 0.8162963867187517 +0.568125 -0.025604248046875 -0.13946533203125 0.8162963867187517 +0.56825 -0.026519775390625 -0.14447021484375 0.8162963867187517 +0.568375 -0.026519775390625 -0.14447021484375 0.8162963867187517 +0.5685 -0.027435302734375 -0.14947509765625 0.8162963867187517 +0.568625 -0.028350830078125 -0.15447998046875 0.8162963867187517 +0.56875 -0.028350830078125 -0.15447998046875 0.8162963867187517 +0.568875 -0.029266357421875 -0.159454345703125 0.8162963867187517 +0.5690000000000001 -0.029266357421875 -0.159454345703125 0.8162963867187517 +0.569125 -0.030181884765625 -0.164398193359375 0.8162963867187517 +0.56925 -0.031097412109375 -0.169342041015625 0.8162963867187517 +0.569375 -0.031097412109375 -0.169342041015625 0.8162963867187517 +0.5695 -0.031982421875 -0.17425537109375 0.8162963867187517 +0.569625 -0.031982421875 -0.17425537109375 0.8162963867187517 +0.56975 -0.03289794921875 -0.17913818359375 0.8162963867187517 +0.569875 -0.033782958984375 -0.18402099609375 0.8162963867187517 +0.5700000000000001 -0.033782958984375 -0.18402099609375 0.8162963867187517 +0.570125 -0.03466796875 -0.188873291015625 0.8162963867187517 +0.57025 -0.03466796875 -0.188873291015625 0.8162963867187517 +0.570375 -0.035552978515625 -0.1937255859375 0.8162963867187517 +0.5705 -0.03643798828125 -0.19854736328125 0.8162963867187517 +0.570625 -0.03643798828125 -0.19854736328125 0.8162963867187517 +0.57075 -0.037322998046875 -0.203338623046875 0.8162963867187517 +0.570875 -0.037322998046875 -0.203338623046875 0.8162963867187517 +0.5710000000000001 -0.0382080078125 -0.208099365234375 0.8162963867187517 +0.571125 -0.0390625 -0.212860107421875 0.8162963867187517 +0.57125 -0.0390625 -0.212860107421875 0.8162963867187517 +0.571375 -0.039947509765625 -0.21759033203125 0.8162963867187517 +0.5715 -0.039947509765625 -0.21759033203125 0.8162963867187517 +0.571625 -0.040802001953125 -0.2222900390625 0.8162963867187517 +0.57175 -0.041656494140625 -0.226959228515625 0.8162963867187517 +0.571875 -0.041656494140625 -0.226959228515625 0.8162963867187517 +0.5720000000000001 -0.042510986328125 -0.231597900390625 0.8162963867187517 +0.572125 -0.042510986328125 -0.231597900390625 0.8162963867187517 +0.57225 -0.043365478515625 -0.236236572265625 0.8162963867187517 +0.572375 -0.044219970703125 -0.2408447265625 0.8162963867187517 +0.5725 -0.044219970703125 -0.2408447265625 0.8162963867187517 +0.5726250000000001 -0.0450439453125 -0.24542236328125 0.8162963867187517 +0.57275 -0.0450439453125 -0.24542236328125 0.8162963867187517 +0.572875 -0.0458984375 -0.249969482421875 0.8162963867187517 +0.5730000000000001 -0.046722412109375 -0.25445556640625 0.8162963867187517 +0.573125 -0.046722412109375 -0.25445556640625 0.8162963867187517 +0.57325 -0.04754638671875 -0.25897216796875 0.8162963867187517 +0.573375 -0.04754638671875 -0.25897216796875 0.8162963867187517 +0.5735 -0.048370361328125 -0.263427734375 0.8162963867187517 +0.5736250000000001 -0.049163818359375 -0.267852783203125 0.8162963867187517 +0.57375 -0.049163818359375 -0.267852783203125 0.8162963867187517 +0.573875 -0.04998779296875 -0.27227783203125 0.8162963867187517 +0.5740000000000001 -0.04998779296875 -0.27227783203125 0.8162963867187517 +0.574125 -0.05078125 -0.276641845703125 0.8162963867187517 +0.57425 -0.05157470703125 -0.280975341796875 0.8162963867187517 +0.574375 -0.05157470703125 -0.280975341796875 0.8162963867187517 +0.5745 -0.0523681640625 -0.285308837890625 0.8162963867187517 +0.5746250000000001 -0.0523681640625 -0.285308837890625 0.8162963867187517 +0.57475 -0.05316162109375 -0.289581298828125 0.8162963867187517 +0.574875 -0.053955078125 -0.2938232421875 0.8162963867187517 +0.5750000000000001 -0.053955078125 -0.2938232421875 0.8162963867187517 +0.575125 -0.054718017578125 -0.298065185546875 0.8162963867187517 +0.57525 -0.054718017578125 -0.298065185546875 0.8162963867187517 +0.575375 -0.05548095703125 -0.30224609375 0.8162963867187517 +0.5755 -0.056243896484375 -0.306396484375 0.8162963867187517 +0.5756250000000001 -0.056243896484375 -0.306396484375 0.8162963867187517 +0.57575 -0.0570068359375 -0.310516357421875 0.8162963867187517 +0.575875 -0.0570068359375 -0.310516357421875 0.8162963867187517 +0.5760000000000001 -0.00714111328125 -0.314605712890625 0.9771972656250006 +0.576125 -0.007232666015625 -0.31866455078125 0.9771972656250006 +0.57625 -0.007232666015625 -0.31866455078125 0.9771972656250006 +0.576375 -0.00732421875 -0.322662353515625 0.9771972656250006 +0.5765 -0.00732421875 -0.322662353515625 0.9771972656250006 +0.5766250000000001 -0.007415771484375 -0.32666015625 0.9771972656250006 +0.57675 -0.007476806640625 -0.330596923828125 0.9771972656250006 +0.576875 -0.007476806640625 -0.330596923828125 0.9771972656250006 +0.577 -0.007568359375 -0.334503173828125 0.9771972656250006 +0.577125 -0.007568359375 -0.334503173828125 0.9771972656250006 +0.57725 -0.007659912109375 -0.33837890625 0.9771972656250006 +0.577375 -0.00775146484375 -0.34222412109375 0.9771972656250006 +0.5775 -0.00775146484375 -0.34222412109375 0.9771972656250006 +0.5776250000000001 -0.007843017578125 -0.34600830078125 0.9771972656250006 +0.57775 -0.007843017578125 -0.34600830078125 0.9771972656250006 +0.577875 -0.0079345703125 -0.349761962890625 0.9771972656250006 +0.578 -0.008026123046875 -0.353485107421875 0.9771972656250006 +0.578125 -0.008026123046875 -0.353485107421875 0.9771972656250006 +0.57825 -0.008087158203125 -0.357177734375 0.9771972656250006 +0.578375 -0.008087158203125 -0.357177734375 0.9771972656250006 +0.5785 -0.0081787109375 -0.360809326171875 0.9771972656250006 +0.5786250000000001 -0.008270263671875 -0.36444091796875 0.9771972656250006 +0.57875 -0.008270263671875 -0.36444091796875 0.9771972656250006 +0.578875 -0.008331298828125 -0.36798095703125 0.9771972656250006 +0.579 -0.008331298828125 -0.36798095703125 0.9771972656250006 +0.579125 -0.0084228515625 -0.37152099609375 0.9771972656250006 +0.57925 -0.008514404296875 -0.375 0.9771972656250006 +0.579375 -0.008514404296875 -0.375 0.9771972656250006 +0.5795 -0.008575439453125 -0.378448486328125 0.9771972656250006 +0.5796250000000001 -0.008575439453125 -0.378448486328125 0.9771972656250006 +0.57975 -0.0086669921875 -0.3818359375 0.9771972656250006 +0.579875 -0.00872802734375 -0.38519287109375 0.9771972656250006 +0.58 -0.00872802734375 -0.38519287109375 0.9771972656250006 +0.580125 -0.008819580078125 -0.388519287109375 0.9771972656250006 +0.58025 -0.008819580078125 -0.388519287109375 0.9771972656250006 +0.580375 -0.008880615234375 -0.39178466796875 0.9771972656250006 +0.5805 -0.008941650390625 -0.39501953125 0.9771972656250006 +0.5806250000000001 -0.008941650390625 -0.39501953125 0.9771972656250006 +0.58075 -0.009033203125 -0.398193359375 0.9771972656250006 +0.580875 -0.009033203125 -0.398193359375 0.9771972656250006 +0.581 -0.00909423828125 -0.401336669921875 0.9771972656250006 +0.581125 -0.0091552734375 -0.404449462890625 0.9771972656250006 +0.58125 -0.0091552734375 -0.404449462890625 0.9771972656250006 +0.581375 -0.009246826171875 -0.407501220703125 0.9771972656250006 +0.5815 -0.009246826171875 -0.407501220703125 0.9771972656250006 +0.5816250000000001 -0.009307861328125 -0.4105224609375 0.9771972656250006 +0.58175 -0.009368896484375 -0.413482666015625 0.9771972656250006 +0.581875 -0.009368896484375 -0.413482666015625 0.9771972656250006 +0.582 -0.009429931640625 -0.416412353515625 0.9771972656250006 +0.582125 -0.009429931640625 -0.416412353515625 0.9771972656250006 +0.58225 -0.009490966796875 -0.419281005859375 0.9771972656250006 +0.582375 -0.00958251953125 -0.422119140625 0.9771972656250006 +0.5825 -0.00958251953125 -0.422119140625 0.9771972656250006 +0.5826250000000001 -0.0096435546875 -0.424896240234375 0.9771972656250006 +0.58275 -0.0096435546875 -0.424896240234375 0.9771972656250006 +0.582875 -0.00970458984375 -0.4276123046875 0.9771972656250006 +0.583 -0.009765625 -0.4302978515625 0.9771972656250006 +0.583125 -0.009765625 -0.4302978515625 0.9771972656250006 +0.58325 -0.00982666015625 -0.432952880859375 0.9771972656250006 +0.583375 -0.00982666015625 -0.432952880859375 0.9771972656250006 +0.5835 -0.0098876953125 -0.435546875 0.9771972656250006 +0.5836250000000001 -0.009918212890625 -0.438079833984375 0.9771972656250006 +0.58375 -0.009918212890625 -0.438079833984375 0.9771972656250006 +0.583875 -0.009979248046875 -0.440582275390625 0.9771972656250006 +0.584 -0.009979248046875 -0.440582275390625 0.9771972656250006 +0.584125 -0.010040283203125 -0.44305419921875 0.9771972656250006 +0.58425 -0.010101318359375 -0.4454345703125 0.9771972656250006 +0.584375 -0.010101318359375 -0.4454345703125 0.9771972656250006 +0.5845 -0.010162353515625 -0.447784423828125 0.9771972656250006 +0.5846250000000001 -0.010162353515625 -0.447784423828125 0.9771972656250006 +0.58475 -0.01019287109375 -0.450103759765625 0.9771972656250006 +0.584875 -0.01025390625 -0.452362060546875 0.9771972656250006 +0.585 -0.01025390625 -0.452362060546875 0.9771972656250006 +0.585125 -0.01031494140625 -0.454559326171875 0.9771972656250006 +0.58525 -0.01031494140625 -0.454559326171875 0.9771972656250006 +0.585375 -0.010345458984375 -0.45672607421875 0.9771972656250006 +0.5855 -0.010406494140625 -0.458831787109375 0.9771972656250006 +0.5856250000000001 -0.010406494140625 -0.458831787109375 0.9771972656250006 +0.58575 -0.01043701171875 -0.46087646484375 0.9771972656250006 +0.585875 -0.01043701171875 -0.46087646484375 0.9771972656250006 +0.586 -0.010498046875 -0.462890625 0.9771972656250006 +0.586125 -0.010528564453125 -0.464813232421875 0.9771972656250006 +0.58625 -0.010528564453125 -0.464813232421875 0.9771972656250006 +0.586375 -0.010589599609375 -0.46673583984375 0.9771972656250006 +0.5865 -0.010589599609375 -0.46673583984375 0.9771972656250006 +0.5866250000000001 -0.0106201171875 -0.46856689453125 0.9771972656250006 +0.58675 -0.010650634765625 -0.470367431640625 0.9771972656250006 +0.586875 -0.010650634765625 -0.470367431640625 0.9771972656250006 +0.587 -0.010711669921875 -0.472137451171875 0.9771972656250006 +0.587125 -0.010711669921875 -0.472137451171875 0.9771972656250006 +0.58725 -0.0107421875 -0.47381591796875 0.9771972656250006 +0.587375 -0.010772705078125 -0.4754638671875 0.9771972656250006 +0.5875 -0.010772705078125 -0.4754638671875 0.9771972656250006 +0.5876250000000001 -0.01080322265625 -0.47705078125 0.9771972656250006 +0.58775 -0.01080322265625 -0.47705078125 0.9771972656250006 +0.587875 -0.0108642578125 -0.478607177734375 0.9771972656250006 +0.588 -0.010894775390625 -0.480072021484375 0.9771972656250006 +0.588125 -0.010894775390625 -0.480072021484375 0.9771972656250006 +0.5882500000000001 -0.01092529296875 -0.48150634765625 0.9771972656250006 +0.588375 -0.01092529296875 -0.48150634765625 0.9771972656250006 +0.5885 -0.010955810546875 -0.48291015625 0.9771972656250006 +0.5886250000000001 -0.010986328125 -0.484222412109375 0.9771972656250006 +0.58875 -0.010986328125 -0.484222412109375 0.9771972656250006 +0.588875 -0.011016845703125 -0.485504150390625 0.9771972656250006 +0.589 -0.011016845703125 -0.485504150390625 0.9771972656250006 +0.589125 -0.01104736328125 -0.486724853515625 0.9771972656250006 +0.5892500000000001 -0.011077880859375 -0.487884521484375 0.9771972656250006 +0.589375 -0.011077880859375 -0.487884521484375 0.9771972656250006 +0.5895 -0.011077880859375 -0.489013671875 0.9771972656250006 +0.5896250000000001 -0.011077880859375 -0.489013671875 0.9771972656250006 +0.58975 -0.0111083984375 -0.490081787109375 0.9771972656250006 +0.589875 -0.011138916015625 -0.4910888671875 0.9771972656250006 +0.59 -0.011138916015625 -0.4910888671875 0.9771972656250006 +0.590125 -0.01116943359375 -0.492034912109375 0.9771972656250006 +0.5902500000000001 -0.01116943359375 -0.492034912109375 0.9771972656250006 +0.590375 -0.01116943359375 -0.492950439453125 0.9771972656250006 +0.5905 -0.011199951171875 -0.4937744140625 0.9771972656250006 +0.5906250000000001 -0.011199951171875 -0.4937744140625 0.9771972656250006 +0.59075 -0.011199951171875 -0.49456787109375 0.9771972656250006 +0.590875 -0.011199951171875 -0.49456787109375 0.9771972656250006 +0.591 -0.01123046875 -0.49530029296875 0.9771972656250006 +0.591125 -0.011260986328125 -0.496002197265625 0.9771972656250006 +0.5912500000000001 -0.011260986328125 -0.496002197265625 0.9771972656250006 +0.591375 -0.011260986328125 -0.496612548828125 0.9771972656250006 +0.5915 -0.011260986328125 -0.496612548828125 0.9771972656250006 +0.5916250000000001 -0.011260986328125 -0.4971923828125 0.9771972656250006 +0.59175 -0.01129150390625 -0.497711181640625 0.9771972656250006 +0.591875 -0.01129150390625 -0.497711181640625 0.9771972656250006 +0.592 -0.01129150390625 -0.498199462890625 0.9771972656250006 +0.592125 -0.01129150390625 -0.498199462890625 0.9771972656250006 +0.5922500000000001 -0.01129150390625 -0.49859619140625 0.9771972656250006 +0.592375 -0.011322021484375 -0.49896240234375 0.9771972656250006 +0.5925 -0.011322021484375 -0.49896240234375 0.9771972656250006 +0.592625 -0.011322021484375 -0.499267578125 0.9771972656250006 +0.59275 -0.011322021484375 -0.499267578125 0.9771972656250006 +0.592875 -0.011322021484375 -0.49951171875 0.9771972656250006 +0.593 -0.011322021484375 -0.49969482421875 0.9771972656250006 +0.593125 -0.011322021484375 -0.49969482421875 0.9771972656250006 +0.5932500000000001 -0.011322021484375 -0.49981689453125 0.9771972656250006 +0.593375 -0.011322021484375 -0.49981689453125 0.9771972656250006 +0.5935 -0.011322021484375 -0.499908447265625 0.9771972656250006 +0.593625 -0.011322021484375 -0.49993896484375 0.9771972656250006 +0.59375 -0.011322021484375 -0.49993896484375 0.9771972656250006 +0.593875 -0.011322021484375 -0.499908447265625 0.9771972656250006 +0.594 -0.011322021484375 -0.499908447265625 0.9771972656250006 +0.594125 -0.011322021484375 -0.49981689453125 0.9771972656250006 +0.5942500000000001 -0.011322021484375 -0.49969482421875 0.9771972656250006 +0.594375 -0.011322021484375 -0.49969482421875 0.9771972656250006 +0.5945 -0.011322021484375 -0.49951171875 0.9771972656250006 +0.594625 -0.011322021484375 -0.49951171875 0.9771972656250006 +0.59475 -0.011322021484375 -0.499267578125 0.9771972656250006 +0.594875 -0.011322021484375 -0.49896240234375 0.9771972656250006 +0.595 -0.011322021484375 -0.49896240234375 0.9771972656250006 +0.595125 -0.01129150390625 -0.49859619140625 0.9771972656250006 +0.5952500000000001 -0.01129150390625 -0.49859619140625 0.9771972656250006 +0.595375 -0.01129150390625 -0.498199462890625 0.9771972656250006 +0.5955 -0.01129150390625 -0.497711181640625 0.9771972656250006 +0.595625 -0.01129150390625 -0.497711181640625 0.9771972656250006 +0.59575 -0.011260986328125 -0.4971923828125 0.9771972656250006 +0.595875 -0.011260986328125 -0.4971923828125 0.9771972656250006 +0.596 -0.011260986328125 -0.496612548828125 0.9771972656250006 +0.596125 -0.011260986328125 -0.496002197265625 0.9771972656250006 +0.5962500000000001 -0.011260986328125 -0.496002197265625 0.9771972656250006 +0.596375 -0.01123046875 -0.49530029296875 0.9771972656250006 +0.5965 -0.01123046875 -0.49530029296875 0.9771972656250006 +0.596625 -0.011199951171875 -0.49456787109375 0.9771972656250006 +0.59675 -0.011199951171875 -0.4937744140625 0.9771972656250006 +0.596875 -0.011199951171875 -0.4937744140625 0.9771972656250006 +0.597 -0.01116943359375 -0.492950439453125 0.9771972656250006 +0.597125 -0.01116943359375 -0.492950439453125 0.9771972656250006 +0.5972500000000001 -0.01116943359375 -0.492034912109375 0.9771972656250006 +0.597375 -0.011138916015625 -0.4910888671875 0.9771972656250006 +0.5975 -0.011138916015625 -0.4910888671875 0.9771972656250006 +0.597625 -0.0111083984375 -0.490081787109375 0.9771972656250006 +0.59775 -0.0111083984375 -0.490081787109375 0.9771972656250006 +0.597875 -0.011077880859375 -0.489013671875 0.9771972656250006 +0.598 -0.011077880859375 -0.487884521484375 0.9771972656250006 +0.598125 -0.011077880859375 -0.487884521484375 0.9771972656250006 +0.5982500000000001 -0.01104736328125 -0.486724853515625 0.9771972656250006 +0.598375 -0.01104736328125 -0.486724853515625 0.9771972656250006 +0.5985 -0.011016845703125 -0.485504150390625 0.9771972656250006 +0.598625 -0.010986328125 -0.484222412109375 0.9771972656250006 +0.59875 -0.010986328125 -0.484222412109375 0.9771972656250006 +0.598875 -0.010955810546875 -0.48291015625 0.9771972656250006 +0.599 -0.010955810546875 -0.48291015625 0.9771972656250006 +0.599125 -0.01092529296875 -0.48150634765625 0.9771972656250006 +0.5992500000000001 -0.010894775390625 -0.480072021484375 0.9771972656250006 +0.599375 -0.010894775390625 -0.480072021484375 0.9771972656250006 +0.5995 -0.0108642578125 -0.478607177734375 0.9771972656250006 +0.599625 -0.0108642578125 -0.478607177734375 0.9771972656250006 +0.59975 -0.01080322265625 -0.47705078125 0.9771972656250006 +0.599875 -0.010772705078125 -0.4754638671875 0.9771972656250006 +0.6 -0.010772705078125 -0.4754638671875 0.9771972656250006 +0.600125 -0.0107421875 -0.47381591796875 0.9771972656250006 +0.6002500000000001 -0.0107421875 -0.47381591796875 0.9771972656250006 +0.600375 -0.010711669921875 -0.472137451171875 0.9771972656250006 +0.6005 -0.010650634765625 -0.470367431640625 0.9771972656250006 +0.600625 -0.010650634765625 -0.470367431640625 0.9771972656250006 +0.60075 -0.0106201171875 -0.46856689453125 0.9771972656250006 +0.600875 -0.0106201171875 -0.46856689453125 0.9771972656250006 +0.601 -0.010589599609375 -0.46673583984375 0.9771972656250006 +0.601125 -0.010528564453125 -0.464813232421875 0.9771972656250006 +0.6012500000000001 -0.010528564453125 -0.464813232421875 0.9771972656250006 +0.601375 -0.010498046875 -0.462890625 0.9771972656250006 +0.6015 -0.010498046875 -0.462890625 0.9771972656250006 +0.601625 -0.01043701171875 -0.46087646484375 0.9771972656250006 +0.60175 -0.010406494140625 -0.458831787109375 0.9771972656250006 +0.601875 -0.010406494140625 -0.458831787109375 0.9771972656250006 +0.602 -0.010345458984375 -0.45672607421875 0.9771972656250006 +0.602125 -0.010345458984375 -0.45672607421875 0.9771972656250006 +0.6022500000000001 -0.01031494140625 -0.454559326171875 0.9771972656250006 +0.602375 -0.01025390625 -0.452362060546875 0.9771972656250006 +0.6025 -0.01025390625 -0.452362060546875 0.9771972656250006 +0.602625 -0.01019287109375 -0.450103759765625 0.9771972656250006 +0.60275 -0.01019287109375 -0.450103759765625 0.9771972656250006 +0.602875 -0.010162353515625 -0.447784423828125 0.9771972656250006 +0.603 -0.010101318359375 -0.4454345703125 0.9771972656250006 +0.603125 -0.010101318359375 -0.4454345703125 0.9771972656250006 +0.6032500000000001 -0.010040283203125 -0.44305419921875 0.9771972656250006 +0.603375 -0.010040283203125 -0.44305419921875 0.9771972656250006 +0.6035 -0.009979248046875 -0.440582275390625 0.9771972656250006 +0.603625 -0.009918212890625 -0.438079833984375 0.9771972656250006 +0.60375 -0.009918212890625 -0.438079833984375 0.9771972656250006 +0.6038750000000001 -0.0098876953125 -0.435546875 0.9771972656250006 +0.604 -0.0098876953125 -0.435546875 0.9771972656250006 +0.604125 -0.00982666015625 -0.432952880859375 0.9771972656250006 +0.6042500000000001 -0.009765625 -0.4302978515625 0.9771972656250006 +0.604375 -0.009765625 -0.4302978515625 0.9771972656250006 +0.6045 -0.00970458984375 -0.4276123046875 0.9771972656250006 +0.604625 -0.00970458984375 -0.4276123046875 0.9771972656250006 +0.60475 -0.0096435546875 -0.424896240234375 0.9771972656250006 +0.6048750000000001 -0.00958251953125 -0.422119140625 0.9771972656250006 +0.605 -0.00958251953125 -0.422119140625 0.9771972656250006 +0.605125 -0.009490966796875 -0.419281005859375 0.9771972656250006 +0.6052500000000001 -0.009490966796875 -0.419281005859375 0.9771972656250006 +0.605375 -0.009429931640625 -0.416412353515625 0.9771972656250006 +0.6055 -0.009368896484375 -0.413482666015625 0.9771972656250006 +0.605625 -0.009368896484375 -0.413482666015625 0.9771972656250006 +0.60575 -0.009307861328125 -0.4105224609375 0.9771972656250006 +0.6058750000000001 -0.009307861328125 -0.4105224609375 0.9771972656250006 +0.606 -0.009246826171875 -0.407501220703125 0.9771972656250006 +0.606125 -0.0091552734375 -0.404449462890625 0.9771972656250006 +0.6062500000000001 -0.0091552734375 -0.404449462890625 0.9771972656250006 +0.606375 -0.00909423828125 -0.401336669921875 0.9771972656250006 +0.6065 -0.00909423828125 -0.401336669921875 0.9771972656250006 +0.606625 -0.009033203125 -0.398193359375 0.9771972656250006 +0.60675 -0.008941650390625 -0.39501953125 0.9771972656250006 +0.6068750000000001 -0.008941650390625 -0.39501953125 0.9771972656250006 +0.607 -0.008880615234375 -0.39178466796875 0.9771972656250006 +0.607125 -0.008880615234375 -0.39178466796875 0.9771972656250006 +0.6072500000000001 -0.008819580078125 -0.388519287109375 0.9771972656250006 +0.607375 -0.00872802734375 -0.38519287109375 0.9771972656250006 +0.6075 -0.00872802734375 -0.38519287109375 0.9771972656250006 +0.607625 -0.0086669921875 -0.3818359375 0.9771972656250006 +0.60775 -0.0086669921875 -0.3818359375 0.9771972656250006 +0.6078750000000001 -0.008575439453125 -0.378448486328125 0.9771972656250006 +0.608 -0.006622314453125 -0.375 0.9822387695312494 +0.608125 -0.006622314453125 -0.375 0.9822387695312494 +0.60825 -0.006561279296875 -0.37152099609375 0.9822387695312494 +0.608375 -0.006561279296875 -0.37152099609375 0.9822387695312494 +0.6085 -0.006500244140625 -0.36798095703125 0.9822387695312494 +0.608625 -0.006439208984375 -0.36444091796875 0.9822387695312494 +0.60875 -0.006439208984375 -0.36444091796875 0.9822387695312494 +0.6088750000000001 -0.00634765625 -0.360809326171875 0.9822387695312494 +0.609 -0.00634765625 -0.360809326171875 0.9822387695312494 +0.609125 -0.00628662109375 -0.357177734375 0.9822387695312494 +0.60925 -0.0062255859375 -0.353485107421875 0.9822387695312494 +0.609375 -0.0062255859375 -0.353485107421875 0.9822387695312494 +0.6095 -0.00616455078125 -0.349761962890625 0.9822387695312494 +0.609625 -0.00616455078125 -0.349761962890625 0.9822387695312494 +0.60975 -0.006103515625 -0.34600830078125 0.9822387695312494 +0.6098750000000001 -0.00604248046875 -0.34222412109375 0.9822387695312494 +0.61 -0.00604248046875 -0.34222412109375 0.9822387695312494 +0.610125 -0.005950927734375 -0.33837890625 0.9822387695312494 +0.61025 -0.005950927734375 -0.33837890625 0.9822387695312494 +0.610375 -0.005889892578125 -0.334503173828125 0.9822387695312494 +0.6105 -0.005828857421875 -0.330596923828125 0.9822387695312494 +0.610625 -0.005828857421875 -0.330596923828125 0.9822387695312494 +0.61075 -0.005767822265625 -0.32666015625 0.9822387695312494 +0.6108750000000001 -0.005767822265625 -0.32666015625 0.9822387695312494 +0.611 -0.00567626953125 -0.322662353515625 0.9822387695312494 +0.611125 -0.005615234375 -0.31866455078125 0.9822387695312494 +0.61125 -0.005615234375 -0.31866455078125 0.9822387695312494 +0.611375 -0.00555419921875 -0.314605712890625 0.9822387695312494 +0.6115 -0.00555419921875 -0.314605712890625 0.9822387695312494 +0.611625 -0.005462646484375 -0.310516357421875 0.9822387695312494 +0.61175 -0.005401611328125 -0.306396484375 0.9822387695312494 +0.6118750000000001 -0.005401611328125 -0.306396484375 0.9822387695312494 +0.612 -0.005340576171875 -0.30224609375 0.9822387695312494 +0.612125 -0.005340576171875 -0.30224609375 0.9822387695312494 +0.61225 -0.0052490234375 -0.298065185546875 0.9822387695312494 +0.612375 -0.00518798828125 -0.2938232421875 0.9822387695312494 +0.6125 -0.00518798828125 -0.2938232421875 0.9822387695312494 +0.612625 -0.005096435546875 -0.289581298828125 0.9822387695312494 +0.61275 -0.005096435546875 -0.289581298828125 0.9822387695312494 +0.6128750000000001 -0.005035400390625 -0.285308837890625 0.9822387695312494 +0.613 -0.00494384765625 -0.280975341796875 0.9822387695312494 +0.613125 -0.00494384765625 -0.280975341796875 0.9822387695312494 +0.61325 -0.0048828125 -0.276641845703125 0.9822387695312494 +0.613375 -0.0048828125 -0.276641845703125 0.9822387695312494 +0.6135 -0.004791259765625 -0.27227783203125 0.9822387695312494 +0.613625 -0.004730224609375 -0.267852783203125 0.9822387695312494 +0.61375 -0.004730224609375 -0.267852783203125 0.9822387695312494 +0.6138750000000001 -0.004638671875 -0.263427734375 0.9822387695312494 +0.614 -0.004638671875 -0.263427734375 0.9822387695312494 +0.614125 -0.004547119140625 -0.25897216796875 0.9822387695312494 +0.61425 -0.004486083984375 -0.25445556640625 0.9822387695312494 +0.614375 -0.004486083984375 -0.25445556640625 0.9822387695312494 +0.6145 -0.00439453125 -0.249969482421875 0.9822387695312494 +0.614625 -0.00439453125 -0.249969482421875 0.9822387695312494 +0.61475 -0.00433349609375 -0.24542236328125 0.9822387695312494 +0.6148750000000001 -0.004241943359375 -0.2408447265625 0.9822387695312494 +0.615 -0.004241943359375 -0.2408447265625 0.9822387695312494 +0.615125 -0.004150390625 -0.236236572265625 0.9822387695312494 +0.61525 -0.004150390625 -0.236236572265625 0.9822387695312494 +0.615375 -0.00408935546875 -0.231597900390625 0.9822387695312494 +0.6155 -0.003997802734375 -0.226959228515625 0.9822387695312494 +0.615625 -0.003997802734375 -0.226959228515625 0.9822387695312494 +0.61575 -0.00390625 -0.2222900390625 0.9822387695312494 +0.6158750000000001 -0.00390625 -0.2222900390625 0.9822387695312494 +0.616 -0.003814697265625 -0.21759033203125 0.9822387695312494 +0.616125 -0.003753662109375 -0.212860107421875 0.9822387695312494 +0.61625 -0.003753662109375 -0.212860107421875 0.9822387695312494 +0.616375 -0.003662109375 -0.208099365234375 0.9822387695312494 +0.6165 -0.003662109375 -0.208099365234375 0.9822387695312494 +0.616625 -0.003570556640625 -0.203338623046875 0.9822387695312494 +0.61675 -0.00347900390625 -0.19854736328125 0.9822387695312494 +0.6168750000000001 -0.00347900390625 -0.19854736328125 0.9822387695312494 +0.617 -0.00341796875 -0.1937255859375 0.9822387695312494 +0.617125 -0.00341796875 -0.1937255859375 0.9822387695312494 +0.61725 -0.003326416015625 -0.188873291015625 0.9822387695312494 +0.617375 -0.00323486328125 -0.18402099609375 0.9822387695312494 +0.6175 -0.00323486328125 -0.18402099609375 0.9822387695312494 +0.617625 -0.003143310546875 -0.17913818359375 0.9822387695312494 +0.61775 -0.003143310546875 -0.17913818359375 0.9822387695312494 +0.6178750000000001 -0.0030517578125 -0.17425537109375 0.9822387695312494 +0.618 -0.00299072265625 -0.169342041015625 0.9822387695312494 +0.618125 -0.00299072265625 -0.169342041015625 0.9822387695312494 +0.61825 -0.002899169921875 -0.164398193359375 0.9822387695312494 +0.618375 -0.002899169921875 -0.164398193359375 0.9822387695312494 +0.6185 -0.0028076171875 -0.159454345703125 0.9822387695312494 +0.618625 -0.002716064453125 -0.15447998046875 0.9822387695312494 +0.61875 -0.002716064453125 -0.15447998046875 0.9822387695312494 +0.6188750000000001 -0.00262451171875 -0.14947509765625 0.9822387695312494 +0.619 -0.00262451171875 -0.14947509765625 0.9822387695312494 +0.619125 -0.002532958984375 -0.14447021484375 0.9822387695312494 +0.61925 -0.00244140625 -0.13946533203125 0.9822387695312494 +0.619375 -0.00244140625 -0.13946533203125 0.9822387695312494 +0.6195000000000001 -0.002349853515625 -0.134429931640625 0.9822387695312494 +0.619625 -0.002349853515625 -0.134429931640625 0.9822387695312494 +0.61975 -0.00225830078125 -0.129364013671875 0.9822387695312494 +0.6198750000000001 -0.002166748046875 -0.124298095703125 0.9822387695312494 +0.62 -0.002166748046875 -0.124298095703125 0.9822387695312494 +0.620125 -0.002105712890625 -0.119232177734375 0.9822387695312494 +0.62025 -0.002105712890625 -0.119232177734375 0.9822387695312494 +0.620375 -0.00201416015625 -0.1141357421875 0.9822387695312494 +0.6205000000000001 -0.001922607421875 -0.109039306640625 0.9822387695312494 +0.620625 -0.001922607421875 -0.109039306640625 0.9822387695312494 +0.62075 -0.0018310546875 -0.103912353515625 0.9822387695312494 +0.6208750000000001 -0.0018310546875 -0.103912353515625 0.9822387695312494 +0.621 -0.001739501953125 -0.098785400390625 0.9822387695312494 +0.621125 -0.00164794921875 -0.093658447265625 0.9822387695312494 +0.62125 -0.00164794921875 -0.093658447265625 0.9822387695312494 +0.621375 -0.001556396484375 -0.0885009765625 0.9822387695312494 +0.6215000000000001 -0.001556396484375 -0.0885009765625 0.9822387695312494 +0.621625 -0.00146484375 -0.083343505859375 0.9822387695312494 +0.62175 -0.001373291015625 -0.07818603515625 0.9822387695312494 +0.6218750000000001 -0.001373291015625 -0.07818603515625 0.9822387695312494 +0.622 -0.00128173828125 -0.072998046875 0.9822387695312494 +0.622125 -0.00128173828125 -0.072998046875 0.9822387695312494 +0.62225 -0.001190185546875 -0.06781005859375 0.9822387695312494 +0.622375 -0.0010986328125 -0.0626220703125 0.9822387695312494 +0.6225000000000001 -0.0010986328125 -0.0626220703125 0.9822387695312494 +0.622625 -0.001007080078125 -0.05743408203125 0.9822387695312494 +0.62275 -0.001007080078125 -0.05743408203125 0.9822387695312494 +0.6228750000000001 -0.00091552734375 -0.05224609375 0.9822387695312494 +0.623 -0.000823974609375 -0.047027587890625 0.9822387695312494 +0.623125 -0.000823974609375 -0.047027587890625 0.9822387695312494 +0.62325 -0.000732421875 -0.04180908203125 0.9822387695312494 +0.623375 -0.000732421875 -0.04180908203125 0.9822387695312494 +0.6235000000000001 -0.000640869140625 -0.036590576171875 0.9822387695312494 +0.623625 -0.00054931640625 -0.0313720703125 0.9822387695312494 +0.62375 -0.00054931640625 -0.0313720703125 0.9822387695312494 +0.6238750000000001 -0.000457763671875 -0.026123046875 0.9822387695312494 +0.624 -0.000457763671875 -0.026123046875 0.9822387695312494 +0.624125 -0.0003662109375 -0.020904541015625 0.9822387695312494 +0.62425 -0.000274658203125 -0.01568603515625 0.9822387695312494 +0.624375 -0.000274658203125 -0.01568603515625 0.9822387695312494 +0.6245000000000001 -0.00018310546875 -0.01043701171875 0.9822387695312494 +0.624625 -0.00018310546875 -0.01043701171875 0.9822387695312494 +0.62475 -9.1552734375e-05 -0.005218505859375 0.9822387695312494 +0.624875 0.0 0.0 0.9822387695312494 +0.625 0.0 0.0 0.9822387695312494 +0.625125 9.1552734375e-05 0.005218505859375 0.9822387695312494 +0.62525 9.1552734375e-05 0.005218505859375 0.9822387695312494 +0.625375 0.00018310546875 0.01043701171875 0.9822387695312494 +0.6255000000000001 0.000274658203125 0.01568603515625 0.9822387695312494 +0.625625 0.000274658203125 0.01568603515625 0.9822387695312494 +0.62575 0.0003662109375 0.020904541015625 0.9822387695312494 +0.625875 0.0003662109375 0.020904541015625 0.9822387695312494 +0.626 0.000457763671875 0.026123046875 0.9822387695312494 +0.626125 0.00054931640625 0.0313720703125 0.9822387695312494 +0.62625 0.00054931640625 0.0313720703125 0.9822387695312494 +0.626375 0.000640869140625 0.036590576171875 0.9822387695312494 +0.6265000000000001 0.000640869140625 0.036590576171875 0.9822387695312494 +0.626625 0.000732421875 0.04180908203125 0.9822387695312494 +0.62675 0.000823974609375 0.047027587890625 0.9822387695312494 +0.626875 0.000823974609375 0.047027587890625 0.9822387695312494 +0.627 0.00091552734375 0.05224609375 0.9822387695312494 +0.627125 0.00091552734375 0.05224609375 0.9822387695312494 +0.62725 0.001007080078125 0.05743408203125 0.9822387695312494 +0.627375 0.0010986328125 0.0626220703125 0.9822387695312494 +0.6275000000000001 0.0010986328125 0.0626220703125 0.9822387695312494 +0.627625 0.001190185546875 0.06781005859375 0.9822387695312494 +0.62775 0.001190185546875 0.06781005859375 0.9822387695312494 +0.627875 0.00128173828125 0.072998046875 0.9822387695312494 +0.628 0.001373291015625 0.07818603515625 0.9822387695312494 +0.628125 0.001373291015625 0.07818603515625 0.9822387695312494 +0.62825 0.00146484375 0.083343505859375 0.9822387695312494 +0.628375 0.00146484375 0.083343505859375 0.9822387695312494 +0.6285000000000001 0.001556396484375 0.0885009765625 0.9822387695312494 +0.628625 0.00164794921875 0.093658447265625 0.9822387695312494 +0.62875 0.00164794921875 0.093658447265625 0.9822387695312494 +0.628875 0.001739501953125 0.098785400390625 0.9822387695312494 +0.629 0.001739501953125 0.098785400390625 0.9822387695312494 +0.629125 0.0018310546875 0.103912353515625 0.9822387695312494 +0.62925 0.001922607421875 0.109039306640625 0.9822387695312494 +0.629375 0.001922607421875 0.109039306640625 0.9822387695312494 +0.6295000000000001 0.00201416015625 0.1141357421875 0.9822387695312494 +0.629625 0.00201416015625 0.1141357421875 0.9822387695312494 +0.62975 0.002105712890625 0.119232177734375 0.9822387695312494 +0.629875 0.002166748046875 0.124298095703125 0.9822387695312494 +0.63 0.002166748046875 0.124298095703125 0.9822387695312494 +0.630125 0.00225830078125 0.129364013671875 0.9822387695312494 +0.63025 0.00225830078125 0.129364013671875 0.9822387695312494 +0.630375 0.002349853515625 0.134429931640625 0.9822387695312494 +0.6305000000000001 0.00244140625 0.13946533203125 0.9822387695312494 +0.630625 0.00244140625 0.13946533203125 0.9822387695312494 +0.63075 0.002532958984375 0.14447021484375 0.9822387695312494 +0.630875 0.002532958984375 0.14447021484375 0.9822387695312494 +0.631 0.00262451171875 0.14947509765625 0.9822387695312494 +0.631125 0.002716064453125 0.15447998046875 0.9822387695312494 +0.63125 0.002716064453125 0.15447998046875 0.9822387695312494 +0.631375 0.0028076171875 0.159454345703125 0.9822387695312494 +0.6315000000000001 0.0028076171875 0.159454345703125 0.9822387695312494 +0.631625 0.002899169921875 0.164398193359375 0.9822387695312494 +0.63175 0.00299072265625 0.169342041015625 0.9822387695312494 +0.631875 0.00299072265625 0.169342041015625 0.9822387695312494 +0.632 0.0030517578125 0.17425537109375 0.9822387695312494 +0.632125 0.0030517578125 0.17425537109375 0.9822387695312494 +0.63225 0.003143310546875 0.17913818359375 0.9822387695312494 +0.632375 0.00323486328125 0.18402099609375 0.9822387695312494 +0.6325000000000001 0.00323486328125 0.18402099609375 0.9822387695312494 +0.632625 0.003326416015625 0.188873291015625 0.9822387695312494 +0.63275 0.003326416015625 0.188873291015625 0.9822387695312494 +0.632875 0.00341796875 0.1937255859375 0.9822387695312494 +0.633 0.00347900390625 0.19854736328125 0.9822387695312494 +0.633125 0.00347900390625 0.19854736328125 0.9822387695312494 +0.63325 0.003570556640625 0.203338623046875 0.9822387695312494 +0.633375 0.003570556640625 0.203338623046875 0.9822387695312494 +0.6335000000000001 0.003662109375 0.208099365234375 0.9822387695312494 +0.633625 0.003753662109375 0.212860107421875 0.9822387695312494 +0.63375 0.003753662109375 0.212860107421875 0.9822387695312494 +0.633875 0.003814697265625 0.21759033203125 0.9822387695312494 +0.634 0.003814697265625 0.21759033203125 0.9822387695312494 +0.634125 0.00390625 0.2222900390625 0.9822387695312494 +0.63425 0.003997802734375 0.226959228515625 0.9822387695312494 +0.634375 0.003997802734375 0.226959228515625 0.9822387695312494 +0.6345000000000001 0.00408935546875 0.231597900390625 0.9822387695312494 +0.634625 0.00408935546875 0.231597900390625 0.9822387695312494 +0.63475 0.004150390625 0.236236572265625 0.9822387695312494 +0.634875 0.004241943359375 0.2408447265625 0.9822387695312494 +0.635 0.004241943359375 0.2408447265625 0.9822387695312494 +0.6351250000000001 0.00433349609375 0.24542236328125 0.9822387695312494 +0.63525 0.00433349609375 0.24542236328125 0.9822387695312494 +0.635375 0.00439453125 0.249969482421875 0.9822387695312494 +0.6355000000000001 0.004486083984375 0.25445556640625 0.9822387695312494 +0.635625 0.004486083984375 0.25445556640625 0.9822387695312494 +0.63575 0.004547119140625 0.25897216796875 0.9822387695312494 +0.635875 0.004547119140625 0.25897216796875 0.9822387695312494 +0.636 0.004638671875 0.263427734375 0.9822387695312494 +0.6361250000000001 0.004730224609375 0.267852783203125 0.9822387695312494 +0.63625 0.004730224609375 0.267852783203125 0.9822387695312494 +0.636375 0.004791259765625 0.27227783203125 0.9822387695312494 +0.6365000000000001 0.004791259765625 0.27227783203125 0.9822387695312494 +0.636625 0.0048828125 0.276641845703125 0.9822387695312494 +0.63675 0.00494384765625 0.280975341796875 0.9822387695312494 +0.636875 0.00494384765625 0.280975341796875 0.9822387695312494 +0.637 0.005035400390625 0.285308837890625 0.9822387695312494 +0.6371250000000001 0.005035400390625 0.285308837890625 0.9822387695312494 +0.63725 0.005096435546875 0.289581298828125 0.9822387695312494 +0.637375 0.00518798828125 0.2938232421875 0.9822387695312494 +0.6375000000000001 0.00518798828125 0.2938232421875 0.9822387695312494 +0.637625 0.0052490234375 0.298065185546875 0.9822387695312494 +0.63775 0.0052490234375 0.298065185546875 0.9822387695312494 +0.637875 0.005340576171875 0.30224609375 0.9822387695312494 +0.638 0.005401611328125 0.306396484375 0.9822387695312494 +0.6381250000000001 0.005401611328125 0.306396484375 0.9822387695312494 +0.63825 0.005462646484375 0.310516357421875 0.9822387695312494 +0.638375 0.005462646484375 0.310516357421875 0.9822387695312494 +0.6385000000000001 0.00555419921875 0.314605712890625 0.9822387695312494 +0.638625 0.005615234375 0.31866455078125 0.9822387695312494 +0.63875 0.005615234375 0.31866455078125 0.9822387695312494 +0.638875 0.00567626953125 0.322662353515625 0.9822387695312494 +0.639 0.00567626953125 0.322662353515625 0.9822387695312494 +0.6391250000000001 0.005767822265625 0.32666015625 0.9822387695312494 +0.63925 0.005828857421875 0.330596923828125 0.9822387695312494 +0.639375 0.005828857421875 0.330596923828125 0.9822387695312494 +0.6395000000000001 0.005889892578125 0.334503173828125 0.9822387695312494 +0.639625 0.005889892578125 0.334503173828125 0.9822387695312494 +0.63975 0.005950927734375 0.33837890625 0.9822387695312494 +0.639875 0.00604248046875 0.34222412109375 0.9822387695312494 +0.64 0.057952880859375 0.34222412109375 0.8305419921874982 +0.6401250000000001 0.05859375 0.34600830078125 0.8305419921874982 +0.64025 0.05859375 0.34600830078125 0.8305419921874982 +0.640375 0.059234619140625 0.349761962890625 0.8305419921874982 +0.6405 0.05987548828125 0.353485107421875 0.8305419921874982 +0.640625 0.05987548828125 0.353485107421875 0.8305419921874982 +0.64075 0.06048583984375 0.357177734375 0.8305419921874982 +0.640875 0.06048583984375 0.357177734375 0.8305419921874982 +0.641 0.06109619140625 0.360809326171875 0.8305419921874982 +0.6411250000000001 0.06170654296875 0.36444091796875 0.8305419921874982 +0.64125 0.06170654296875 0.36444091796875 0.8305419921874982 +0.641375 0.06231689453125 0.36798095703125 0.8305419921874982 +0.6415 0.06231689453125 0.36798095703125 0.8305419921874982 +0.641625 0.06292724609375 0.37152099609375 0.8305419921874982 +0.64175 0.063507080078125 0.375 0.8305419921874982 +0.641875 0.063507080078125 0.375 0.8305419921874982 +0.642 0.0640869140625 0.378448486328125 0.8305419921874982 +0.6421250000000001 0.0640869140625 0.378448486328125 0.8305419921874982 +0.64225 0.064666748046875 0.3818359375 0.8305419921874982 +0.642375 0.06524658203125 0.38519287109375 0.8305419921874982 +0.6425 0.06524658203125 0.38519287109375 0.8305419921874982 +0.642625 0.0657958984375 0.388519287109375 0.8305419921874982 +0.64275 0.0657958984375 0.388519287109375 0.8305419921874982 +0.642875 0.06634521484375 0.39178466796875 0.8305419921874982 +0.643 0.06689453125 0.39501953125 0.8305419921874982 +0.6431250000000001 0.06689453125 0.39501953125 0.8305419921874982 +0.64325 0.06744384765625 0.398193359375 0.8305419921874982 +0.643375 0.06744384765625 0.398193359375 0.8305419921874982 +0.6435 0.067962646484375 0.401336669921875 0.8305419921874982 +0.643625 0.0684814453125 0.404449462890625 0.8305419921874982 +0.64375 0.0684814453125 0.404449462890625 0.8305419921874982 +0.643875 0.069000244140625 0.407501220703125 0.8305419921874982 +0.644 0.069000244140625 0.407501220703125 0.8305419921874982 +0.6441250000000001 0.06951904296875 0.4105224609375 0.8305419921874982 +0.64425 0.07000732421875 0.413482666015625 0.8305419921874982 +0.644375 0.07000732421875 0.413482666015625 0.8305419921874982 +0.6445 0.070526123046875 0.416412353515625 0.8305419921874982 +0.644625 0.070526123046875 0.416412353515625 0.8305419921874982 +0.64475 0.071014404296875 0.419281005859375 0.8305419921874982 +0.644875 0.07147216796875 0.422119140625 0.8305419921874982 +0.645 0.07147216796875 0.422119140625 0.8305419921874982 +0.6451250000000001 0.07196044921875 0.424896240234375 0.8305419921874982 +0.64525 0.07196044921875 0.424896240234375 0.8305419921874982 +0.645375 0.072418212890625 0.4276123046875 0.8305419921874982 +0.6455 0.0728759765625 0.4302978515625 0.8305419921874982 +0.645625 0.0728759765625 0.4302978515625 0.8305419921874982 +0.64575 0.073333740234375 0.432952880859375 0.8305419921874982 +0.645875 0.073333740234375 0.432952880859375 0.8305419921874982 +0.646 0.073760986328125 0.435546875 0.8305419921874982 +0.6461250000000001 0.074188232421875 0.438079833984375 0.8305419921874982 +0.64625 0.074188232421875 0.438079833984375 0.8305419921874982 +0.646375 0.074615478515625 0.440582275390625 0.8305419921874982 +0.6465 0.074615478515625 0.440582275390625 0.8305419921874982 +0.646625 0.075042724609375 0.44305419921875 0.8305419921874982 +0.64675 0.075439453125 0.4454345703125 0.8305419921874982 +0.646875 0.075439453125 0.4454345703125 0.8305419921874982 +0.647 0.075836181640625 0.447784423828125 0.8305419921874982 +0.6471250000000001 0.075836181640625 0.447784423828125 0.8305419921874982 +0.64725 0.07623291015625 0.450103759765625 0.8305419921874982 +0.647375 0.07659912109375 0.452362060546875 0.8305419921874982 +0.6475 0.07659912109375 0.452362060546875 0.8305419921874982 +0.647625 0.07696533203125 0.454559326171875 0.8305419921874982 +0.64775 0.07696533203125 0.454559326171875 0.8305419921874982 +0.647875 0.07733154296875 0.45672607421875 0.8305419921874982 +0.648 0.07769775390625 0.458831787109375 0.8305419921874982 +0.6481250000000001 0.07769775390625 0.458831787109375 0.8305419921874982 +0.64825 0.07806396484375 0.46087646484375 0.8305419921874982 +0.648375 0.07806396484375 0.46087646484375 0.8305419921874982 +0.6485 0.078399658203125 0.462890625 0.8305419921874982 +0.648625 0.078704833984375 0.464813232421875 0.8305419921874982 +0.64875 0.078704833984375 0.464813232421875 0.8305419921874982 +0.648875 0.07904052734375 0.46673583984375 0.8305419921874982 +0.649 0.07904052734375 0.46673583984375 0.8305419921874982 +0.6491250000000001 0.079345703125 0.46856689453125 0.8305419921874982 +0.64925 0.07965087890625 0.470367431640625 0.8305419921874982 +0.649375 0.07965087890625 0.470367431640625 0.8305419921874982 +0.6495 0.0799560546875 0.472137451171875 0.8305419921874982 +0.649625 0.0799560546875 0.472137451171875 0.8305419921874982 +0.64975 0.080230712890625 0.47381591796875 0.8305419921874982 +0.649875 0.080535888671875 0.4754638671875 0.8305419921874982 +0.65 0.080535888671875 0.4754638671875 0.8305419921874982 +0.6501250000000001 0.080780029296875 0.47705078125 0.8305419921874982 +0.65025 0.080780029296875 0.47705078125 0.8305419921874982 +0.650375 0.0810546875 0.478607177734375 0.8305419921874982 +0.6505 0.081298828125 0.480072021484375 0.8305419921874982 +0.650625 0.081298828125 0.480072021484375 0.8305419921874982 +0.6507500000000001 0.08154296875 0.48150634765625 0.8305419921874982 +0.650875 0.08154296875 0.48150634765625 0.8305419921874982 +0.651 0.081787109375 0.48291015625 0.8305419921874982 +0.6511250000000001 0.082000732421875 0.484222412109375 0.8305419921874982 +0.65125 0.082000732421875 0.484222412109375 0.8305419921874982 +0.651375 0.08221435546875 0.485504150390625 0.8305419921874982 +0.6515 0.08221435546875 0.485504150390625 0.8305419921874982 +0.651625 0.082427978515625 0.486724853515625 0.8305419921874982 +0.6517500000000001 0.0826416015625 0.487884521484375 0.8305419921874982 +0.651875 0.0826416015625 0.487884521484375 0.8305419921874982 +0.652 0.08282470703125 0.489013671875 0.8305419921874982 +0.6521250000000001 0.08282470703125 0.489013671875 0.8305419921874982 +0.65225 0.0830078125 0.490081787109375 0.8305419921874982 +0.652375 0.083160400390625 0.4910888671875 0.8305419921874982 +0.6525 0.083160400390625 0.4910888671875 0.8305419921874982 +0.652625 0.083343505859375 0.492034912109375 0.8305419921874982 +0.6527500000000001 0.083343505859375 0.492034912109375 0.8305419921874982 +0.652875 0.08349609375 0.492950439453125 0.8305419921874982 +0.653 0.0836181640625 0.4937744140625 0.8305419921874982 +0.6531250000000001 0.0836181640625 0.4937744140625 0.8305419921874982 +0.65325 0.083770751953125 0.49456787109375 0.8305419921874982 +0.653375 0.083770751953125 0.49456787109375 0.8305419921874982 +0.6535 0.083892822265625 0.49530029296875 0.8305419921874982 +0.653625 0.083984375 0.496002197265625 0.8305419921874982 +0.6537500000000001 0.083984375 0.496002197265625 0.8305419921874982 +0.653875 0.0841064453125 0.496612548828125 0.8305419921874982 +0.654 0.0841064453125 0.496612548828125 0.8305419921874982 +0.6541250000000001 0.084197998046875 0.4971923828125 0.8305419921874982 +0.65425 0.08428955078125 0.497711181640625 0.8305419921874982 +0.654375 0.08428955078125 0.497711181640625 0.8305419921874982 +0.6545 0.084381103515625 0.498199462890625 0.8305419921874982 +0.654625 0.084381103515625 0.498199462890625 0.8305419921874982 +0.6547500000000001 0.084442138671875 0.49859619140625 0.8305419921874982 +0.654875 0.084503173828125 0.49896240234375 0.8305419921874982 +0.655 0.084503173828125 0.49896240234375 0.8305419921874982 +0.6551250000000001 0.084564208984375 0.499267578125 0.8305419921874982 +0.65525 0.084564208984375 0.499267578125 0.8305419921874982 +0.655375 0.0845947265625 0.49951171875 0.8305419921874982 +0.6555 0.084625244140625 0.49969482421875 0.8305419921874982 +0.655625 0.084625244140625 0.49969482421875 0.8305419921874982 +0.6557500000000001 0.08465576171875 0.49981689453125 0.8305419921874982 +0.655875 0.08465576171875 0.49981689453125 0.8305419921874982 +0.656 0.08465576171875 0.499908447265625 0.8305419921874982 +0.656125 0.08465576171875 0.49993896484375 0.8305419921874982 +0.65625 0.08465576171875 0.49993896484375 0.8305419921874982 +0.656375 0.08465576171875 0.499908447265625 0.8305419921874982 +0.6565 0.08465576171875 0.499908447265625 0.8305419921874982 +0.656625 0.08465576171875 0.49981689453125 0.8305419921874982 +0.6567500000000001 0.084625244140625 0.49969482421875 0.8305419921874982 +0.656875 0.084625244140625 0.49969482421875 0.8305419921874982 +0.657 0.0845947265625 0.49951171875 0.8305419921874982 +0.657125 0.0845947265625 0.49951171875 0.8305419921874982 +0.65725 0.084564208984375 0.499267578125 0.8305419921874982 +0.657375 0.084503173828125 0.49896240234375 0.8305419921874982 +0.6575 0.084503173828125 0.49896240234375 0.8305419921874982 +0.657625 0.084442138671875 0.49859619140625 0.8305419921874982 +0.6577500000000001 0.084442138671875 0.49859619140625 0.8305419921874982 +0.657875 0.084381103515625 0.498199462890625 0.8305419921874982 +0.658 0.08428955078125 0.497711181640625 0.8305419921874982 +0.658125 0.08428955078125 0.497711181640625 0.8305419921874982 +0.65825 0.084197998046875 0.4971923828125 0.8305419921874982 +0.658375 0.084197998046875 0.4971923828125 0.8305419921874982 +0.6585 0.0841064453125 0.496612548828125 0.8305419921874982 +0.658625 0.083984375 0.496002197265625 0.8305419921874982 +0.6587500000000001 0.083984375 0.496002197265625 0.8305419921874982 +0.658875 0.083892822265625 0.49530029296875 0.8305419921874982 +0.659 0.083892822265625 0.49530029296875 0.8305419921874982 +0.659125 0.083770751953125 0.49456787109375 0.8305419921874982 +0.65925 0.0836181640625 0.4937744140625 0.8305419921874982 +0.659375 0.0836181640625 0.4937744140625 0.8305419921874982 +0.6595 0.08349609375 0.492950439453125 0.8305419921874982 +0.659625 0.08349609375 0.492950439453125 0.8305419921874982 +0.6597500000000001 0.083343505859375 0.492034912109375 0.8305419921874982 +0.659875 0.083160400390625 0.4910888671875 0.8305419921874982 +0.66 0.083160400390625 0.4910888671875 0.8305419921874982 +0.660125 0.0830078125 0.490081787109375 0.8305419921874982 +0.66025 0.0830078125 0.490081787109375 0.8305419921874982 +0.660375 0.08282470703125 0.489013671875 0.8305419921874982 +0.6605 0.0826416015625 0.487884521484375 0.8305419921874982 +0.660625 0.0826416015625 0.487884521484375 0.8305419921874982 +0.6607500000000001 0.082427978515625 0.486724853515625 0.8305419921874982 +0.660875 0.082427978515625 0.486724853515625 0.8305419921874982 +0.661 0.08221435546875 0.485504150390625 0.8305419921874982 +0.661125 0.082000732421875 0.484222412109375 0.8305419921874982 +0.66125 0.082000732421875 0.484222412109375 0.8305419921874982 +0.661375 0.081787109375 0.48291015625 0.8305419921874982 +0.6615 0.081787109375 0.48291015625 0.8305419921874982 +0.661625 0.08154296875 0.48150634765625 0.8305419921874982 +0.6617500000000001 0.081298828125 0.480072021484375 0.8305419921874982 +0.661875 0.081298828125 0.480072021484375 0.8305419921874982 +0.662 0.0810546875 0.478607177734375 0.8305419921874982 +0.662125 0.0810546875 0.478607177734375 0.8305419921874982 +0.66225 0.080780029296875 0.47705078125 0.8305419921874982 +0.662375 0.080535888671875 0.4754638671875 0.8305419921874982 +0.6625 0.080535888671875 0.4754638671875 0.8305419921874982 +0.662625 0.080230712890625 0.47381591796875 0.8305419921874982 +0.6627500000000001 0.080230712890625 0.47381591796875 0.8305419921874982 +0.662875 0.0799560546875 0.472137451171875 0.8305419921874982 +0.663 0.07965087890625 0.470367431640625 0.8305419921874982 +0.663125 0.07965087890625 0.470367431640625 0.8305419921874982 +0.66325 0.079345703125 0.46856689453125 0.8305419921874982 +0.663375 0.079345703125 0.46856689453125 0.8305419921874982 +0.6635 0.07904052734375 0.46673583984375 0.8305419921874982 +0.663625 0.078704833984375 0.464813232421875 0.8305419921874982 +0.6637500000000001 0.078704833984375 0.464813232421875 0.8305419921874982 +0.663875 0.078399658203125 0.462890625 0.8305419921874982 +0.664 0.078399658203125 0.462890625 0.8305419921874982 +0.664125 0.07806396484375 0.46087646484375 0.8305419921874982 +0.66425 0.07769775390625 0.458831787109375 0.8305419921874982 +0.664375 0.07769775390625 0.458831787109375 0.8305419921874982 +0.6645 0.07733154296875 0.45672607421875 0.8305419921874982 +0.664625 0.07733154296875 0.45672607421875 0.8305419921874982 +0.6647500000000001 0.07696533203125 0.454559326171875 0.8305419921874982 +0.664875 0.07659912109375 0.452362060546875 0.8305419921874982 +0.665 0.07659912109375 0.452362060546875 0.8305419921874982 +0.665125 0.07623291015625 0.450103759765625 0.8305419921874982 +0.66525 0.07623291015625 0.450103759765625 0.8305419921874982 +0.665375 0.075836181640625 0.447784423828125 0.8305419921874982 +0.6655 0.075439453125 0.4454345703125 0.8305419921874982 +0.665625 0.075439453125 0.4454345703125 0.8305419921874982 +0.6657500000000001 0.075042724609375 0.44305419921875 0.8305419921874982 +0.665875 0.075042724609375 0.44305419921875 0.8305419921874982 +0.666 0.074615478515625 0.440582275390625 0.8305419921874982 +0.666125 0.074188232421875 0.438079833984375 0.8305419921874982 +0.66625 0.074188232421875 0.438079833984375 0.8305419921874982 +0.6663750000000001 0.073760986328125 0.435546875 0.8305419921874982 +0.6665 0.073760986328125 0.435546875 0.8305419921874982 +0.666625 0.073333740234375 0.432952880859375 0.8305419921874982 +0.6667500000000001 0.0728759765625 0.4302978515625 0.8305419921874982 +0.666875 0.0728759765625 0.4302978515625 0.8305419921874982 +0.667 0.072418212890625 0.4276123046875 0.8305419921874982 +0.667125 0.072418212890625 0.4276123046875 0.8305419921874982 +0.66725 0.07196044921875 0.424896240234375 0.8305419921874982 +0.6673750000000001 0.07147216796875 0.422119140625 0.8305419921874982 +0.6675 0.07147216796875 0.422119140625 0.8305419921874982 +0.667625 0.071014404296875 0.419281005859375 0.8305419921874982 +0.6677500000000001 0.071014404296875 0.419281005859375 0.8305419921874982 +0.667875 0.070526123046875 0.416412353515625 0.8305419921874982 +0.668 0.07000732421875 0.413482666015625 0.8305419921874982 +0.668125 0.07000732421875 0.413482666015625 0.8305419921874982 +0.66825 0.06951904296875 0.4105224609375 0.8305419921874982 +0.6683750000000001 0.06951904296875 0.4105224609375 0.8305419921874982 +0.6685 0.069000244140625 0.407501220703125 0.8305419921874982 +0.668625 0.0684814453125 0.404449462890625 0.8305419921874982 +0.6687500000000001 0.0684814453125 0.404449462890625 0.8305419921874982 +0.668875 0.067962646484375 0.401336669921875 0.8305419921874982 +0.669 0.067962646484375 0.401336669921875 0.8305419921874982 +0.669125 0.06744384765625 0.398193359375 0.8305419921874982 +0.66925 0.06689453125 0.39501953125 0.8305419921874982 +0.6693750000000001 0.06689453125 0.39501953125 0.8305419921874982 +0.6695 0.06634521484375 0.39178466796875 0.8305419921874982 +0.669625 0.06634521484375 0.39178466796875 0.8305419921874982 +0.6697500000000001 0.0657958984375 0.388519287109375 0.8305419921874982 +0.669875 0.06524658203125 0.38519287109375 0.8305419921874982 +0.67 0.06524658203125 0.38519287109375 0.8305419921874982 +0.670125 0.064666748046875 0.3818359375 0.8305419921874982 +0.67025 0.064666748046875 0.3818359375 0.8305419921874982 +0.6703750000000001 0.0640869140625 0.378448486328125 0.8305419921874982 +0.6705 0.063507080078125 0.375 0.8305419921874982 +0.670625 0.063507080078125 0.375 0.8305419921874982 +0.6707500000000001 0.06292724609375 0.37152099609375 0.8305419921874982 +0.670875 0.06292724609375 0.37152099609375 0.8305419921874982 +0.671 0.06231689453125 0.36798095703125 0.8305419921874982 +0.671125 0.06170654296875 0.36444091796875 0.8305419921874982 +0.67125 0.06170654296875 0.36444091796875 0.8305419921874982 +0.6713750000000001 0.06109619140625 0.360809326171875 0.8305419921874982 +0.6715 0.06109619140625 0.360809326171875 0.8305419921874982 +0.671625 0.06048583984375 0.357177734375 0.8305419921874982 +0.6717500000000001 0.05987548828125 0.353485107421875 0.8305419921874982 +0.671875 0.05987548828125 0.353485107421875 0.8305419921874982 +0.672 0.15863037109375 0.349761962890625 0.5463623046874974 +0.672125 0.15863037109375 0.349761962890625 0.5463623046874974 +0.67225 0.156951904296875 0.34600830078125 0.5463623046874974 +0.6723750000000001 0.15521240234375 0.34222412109375 0.5463623046874974 +0.6725 0.15521240234375 0.34222412109375 0.5463623046874974 +0.672625 0.153472900390625 0.33837890625 0.5463623046874974 +0.67275 0.153472900390625 0.33837890625 0.5463623046874974 +0.672875 0.1517333984375 0.334503173828125 0.5463623046874974 +0.673 0.14996337890625 0.330596923828125 0.5463623046874974 +0.673125 0.14996337890625 0.330596923828125 0.5463623046874974 +0.67325 0.148162841796875 0.32666015625 0.5463623046874974 +0.6733750000000001 0.148162841796875 0.32666015625 0.5463623046874974 +0.6735 0.1463623046875 0.322662353515625 0.5463623046874974 +0.673625 0.14453125 0.31866455078125 0.5463623046874974 +0.67375 0.14453125 0.31866455078125 0.5463623046874974 +0.673875 0.1427001953125 0.314605712890625 0.5463623046874974 +0.674 0.1427001953125 0.314605712890625 0.5463623046874974 +0.674125 0.140838623046875 0.310516357421875 0.5463623046874974 +0.67425 0.13897705078125 0.306396484375 0.5463623046874974 +0.6743750000000001 0.13897705078125 0.306396484375 0.5463623046874974 +0.6745 0.1370849609375 0.30224609375 0.5463623046874974 +0.674625 0.1370849609375 0.30224609375 0.5463623046874974 +0.67475 0.13519287109375 0.298065185546875 0.5463623046874974 +0.674875 0.133270263671875 0.2938232421875 0.5463623046874974 +0.675 0.133270263671875 0.2938232421875 0.5463623046874974 +0.675125 0.13134765625 0.289581298828125 0.5463623046874974 +0.67525 0.13134765625 0.289581298828125 0.5463623046874974 +0.6753750000000001 0.12939453125 0.285308837890625 0.5463623046874974 +0.6755 0.12744140625 0.280975341796875 0.5463623046874974 +0.675625 0.12744140625 0.280975341796875 0.5463623046874974 +0.67575 0.125457763671875 0.276641845703125 0.5463623046874974 +0.675875 0.125457763671875 0.276641845703125 0.5463623046874974 +0.676 0.123504638671875 0.27227783203125 0.5463623046874974 +0.676125 0.121490478515625 0.267852783203125 0.5463623046874974 +0.67625 0.121490478515625 0.267852783203125 0.5463623046874974 +0.6763750000000001 0.119476318359375 0.263427734375 0.5463623046874974 +0.6765 0.119476318359375 0.263427734375 0.5463623046874974 +0.676625 0.117462158203125 0.25897216796875 0.5463623046874974 +0.67675 0.11541748046875 0.25445556640625 0.5463623046874974 +0.676875 0.11541748046875 0.25445556640625 0.5463623046874974 +0.677 0.113372802734375 0.249969482421875 0.5463623046874974 +0.677125 0.113372802734375 0.249969482421875 0.5463623046874974 +0.67725 0.111297607421875 0.24542236328125 0.5463623046874974 +0.6773750000000001 0.109222412109375 0.2408447265625 0.5463623046874974 +0.6775 0.109222412109375 0.2408447265625 0.5463623046874974 +0.677625 0.107147216796875 0.236236572265625 0.5463623046874974 +0.67775 0.107147216796875 0.236236572265625 0.5463623046874974 +0.677875 0.10504150390625 0.231597900390625 0.5463623046874974 +0.678 0.102935791015625 0.226959228515625 0.5463623046874974 +0.678125 0.102935791015625 0.226959228515625 0.5463623046874974 +0.67825 0.100799560546875 0.2222900390625 0.5463623046874974 +0.6783750000000001 0.100799560546875 0.2222900390625 0.5463623046874974 +0.6785 0.09869384765625 0.21759033203125 0.5463623046874974 +0.678625 0.096527099609375 0.212860107421875 0.5463623046874974 +0.67875 0.096527099609375 0.212860107421875 0.5463623046874974 +0.678875 0.094390869140625 0.208099365234375 0.5463623046874974 +0.679 0.094390869140625 0.208099365234375 0.5463623046874974 +0.679125 0.09222412109375 0.203338623046875 0.5463623046874974 +0.67925 0.090057373046875 0.19854736328125 0.5463623046874974 +0.6793750000000001 0.090057373046875 0.19854736328125 0.5463623046874974 +0.6795 0.087860107421875 0.1937255859375 0.5463623046874974 +0.679625 0.087860107421875 0.1937255859375 0.5463623046874974 +0.67975 0.085662841796875 0.188873291015625 0.5463623046874974 +0.679875 0.083465576171875 0.18402099609375 0.5463623046874974 +0.68 0.083465576171875 0.18402099609375 0.5463623046874974 +0.680125 0.08123779296875 0.17913818359375 0.5463623046874974 +0.68025 0.08123779296875 0.17913818359375 0.5463623046874974 +0.6803750000000001 0.07904052734375 0.17425537109375 0.5463623046874974 +0.6805 0.0767822265625 0.169342041015625 0.5463623046874974 +0.680625 0.0767822265625 0.169342041015625 0.5463623046874974 +0.68075 0.074554443359375 0.164398193359375 0.5463623046874974 +0.680875 0.074554443359375 0.164398193359375 0.5463623046874974 +0.681 0.07232666015625 0.159454345703125 0.5463623046874974 +0.681125 0.070068359375 0.15447998046875 0.5463623046874974 +0.68125 0.070068359375 0.15447998046875 0.5463623046874974 +0.6813750000000001 0.067779541015625 0.14947509765625 0.5463623046874974 +0.6815 0.067779541015625 0.14947509765625 0.5463623046874974 +0.681625 0.065521240234375 0.14447021484375 0.5463623046874974 +0.68175 0.063232421875 0.13946533203125 0.5463623046874974 +0.681875 0.063232421875 0.13946533203125 0.5463623046874974 +0.6820000000000001 0.06097412109375 0.134429931640625 0.5463623046874974 +0.682125 0.06097412109375 0.134429931640625 0.5463623046874974 +0.68225 0.058685302734375 0.129364013671875 0.5463623046874974 +0.6823750000000001 0.056365966796875 0.124298095703125 0.5463623046874974 +0.6825 0.056365966796875 0.124298095703125 0.5463623046874974 +0.682625 0.0540771484375 0.119232177734375 0.5463623046874974 +0.68275 0.0540771484375 0.119232177734375 0.5463623046874974 +0.682875 0.0517578125 0.1141357421875 0.5463623046874974 +0.6830000000000001 0.0494384765625 0.109039306640625 0.5463623046874974 +0.683125 0.0494384765625 0.109039306640625 0.5463623046874974 +0.68325 0.047119140625 0.103912353515625 0.5463623046874974 +0.6833750000000001 0.047119140625 0.103912353515625 0.5463623046874974 +0.6835 0.0447998046875 0.098785400390625 0.5463623046874974 +0.683625 0.04248046875 0.093658447265625 0.5463623046874974 +0.68375 0.04248046875 0.093658447265625 0.5463623046874974 +0.683875 0.040130615234375 0.0885009765625 0.5463623046874974 +0.6840000000000001 0.040130615234375 0.0885009765625 0.5463623046874974 +0.684125 0.037811279296875 0.083343505859375 0.5463623046874974 +0.68425 0.03546142578125 0.07818603515625 0.5463623046874974 +0.6843750000000001 0.03546142578125 0.07818603515625 0.5463623046874974 +0.6845 0.033111572265625 0.072998046875 0.5463623046874974 +0.684625 0.033111572265625 0.072998046875 0.5463623046874974 +0.68475 0.03076171875 0.06781005859375 0.5463623046874974 +0.684875 0.028411865234375 0.0626220703125 0.5463623046874974 +0.6850000000000001 0.028411865234375 0.0626220703125 0.5463623046874974 +0.685125 0.02606201171875 0.05743408203125 0.5463623046874974 +0.68525 0.02606201171875 0.05743408203125 0.5463623046874974 +0.6853750000000001 0.023681640625 0.05224609375 0.5463623046874974 +0.6855 0.021331787109375 0.047027587890625 0.5463623046874974 +0.685625 0.021331787109375 0.047027587890625 0.5463623046874974 +0.68575 0.018951416015625 0.04180908203125 0.5463623046874974 +0.685875 0.018951416015625 0.04180908203125 0.5463623046874974 +0.6860000000000001 0.0166015625 0.036590576171875 0.5463623046874974 +0.686125 0.01422119140625 0.0313720703125 0.5463623046874974 +0.68625 0.01422119140625 0.0313720703125 0.5463623046874974 +0.6863750000000001 0.0118408203125 0.026123046875 0.5463623046874974 +0.6865 0.0118408203125 0.026123046875 0.5463623046874974 +0.686625 0.009490966796875 0.020904541015625 0.5463623046874974 +0.68675 0.007110595703125 0.01568603515625 0.5463623046874974 +0.686875 0.007110595703125 0.01568603515625 0.5463623046874974 +0.6870000000000001 0.004730224609375 0.01043701171875 0.5463623046874974 +0.687125 0.004730224609375 0.01043701171875 0.5463623046874974 +0.68725 0.002349853515625 0.005218505859375 0.5463623046874974 +0.6873750000000001 0.0 0.0 0.5463623046874974 +0.6875 0.0 0.0 0.5463623046874974 +0.687625 -0.002349853515625 -0.005218505859375 0.5463623046874974 +0.68775 -0.002349853515625 -0.005218505859375 0.5463623046874974 +0.687875 -0.004730224609375 -0.01043701171875 0.5463623046874974 +0.6880000000000001 -0.007110595703125 -0.01568603515625 0.5463623046874974 +0.688125 -0.007110595703125 -0.01568603515625 0.5463623046874974 +0.68825 -0.009490966796875 -0.020904541015625 0.5463623046874974 +0.688375 -0.009490966796875 -0.020904541015625 0.5463623046874974 +0.6885 -0.0118408203125 -0.026123046875 0.5463623046874974 +0.688625 -0.01422119140625 -0.0313720703125 0.5463623046874974 +0.68875 -0.01422119140625 -0.0313720703125 0.5463623046874974 +0.688875 -0.0166015625 -0.036590576171875 0.5463623046874974 +0.6890000000000001 -0.0166015625 -0.036590576171875 0.5463623046874974 +0.689125 -0.018951416015625 -0.04180908203125 0.5463623046874974 +0.68925 -0.021331787109375 -0.047027587890625 0.5463623046874974 +0.689375 -0.021331787109375 -0.047027587890625 0.5463623046874974 +0.6895 -0.023681640625 -0.05224609375 0.5463623046874974 +0.689625 -0.023681640625 -0.05224609375 0.5463623046874974 +0.68975 -0.02606201171875 -0.05743408203125 0.5463623046874974 +0.689875 -0.028411865234375 -0.0626220703125 0.5463623046874974 +0.6900000000000001 -0.028411865234375 -0.0626220703125 0.5463623046874974 +0.690125 -0.03076171875 -0.06781005859375 0.5463623046874974 +0.69025 -0.03076171875 -0.06781005859375 0.5463623046874974 +0.690375 -0.033111572265625 -0.072998046875 0.5463623046874974 +0.6905 -0.03546142578125 -0.07818603515625 0.5463623046874974 +0.690625 -0.03546142578125 -0.07818603515625 0.5463623046874974 +0.69075 -0.037811279296875 -0.083343505859375 0.5463623046874974 +0.690875 -0.037811279296875 -0.083343505859375 0.5463623046874974 +0.6910000000000001 -0.040130615234375 -0.0885009765625 0.5463623046874974 +0.691125 -0.04248046875 -0.093658447265625 0.5463623046874974 +0.69125 -0.04248046875 -0.093658447265625 0.5463623046874974 +0.691375 -0.0447998046875 -0.098785400390625 0.5463623046874974 +0.6915 -0.0447998046875 -0.098785400390625 0.5463623046874974 +0.691625 -0.047119140625 -0.103912353515625 0.5463623046874974 +0.69175 -0.0494384765625 -0.109039306640625 0.5463623046874974 +0.691875 -0.0494384765625 -0.109039306640625 0.5463623046874974 +0.6920000000000001 -0.0517578125 -0.1141357421875 0.5463623046874974 +0.692125 -0.0517578125 -0.1141357421875 0.5463623046874974 +0.69225 -0.0540771484375 -0.119232177734375 0.5463623046874974 +0.692375 -0.056365966796875 -0.124298095703125 0.5463623046874974 +0.6925 -0.056365966796875 -0.124298095703125 0.5463623046874974 +0.692625 -0.058685302734375 -0.129364013671875 0.5463623046874974 +0.69275 -0.058685302734375 -0.129364013671875 0.5463623046874974 +0.692875 -0.06097412109375 -0.134429931640625 0.5463623046874974 +0.6930000000000001 -0.063232421875 -0.13946533203125 0.5463623046874974 +0.693125 -0.063232421875 -0.13946533203125 0.5463623046874974 +0.69325 -0.065521240234375 -0.14447021484375 0.5463623046874974 +0.693375 -0.065521240234375 -0.14447021484375 0.5463623046874974 +0.6935 -0.067779541015625 -0.14947509765625 0.5463623046874974 +0.693625 -0.070068359375 -0.15447998046875 0.5463623046874974 +0.69375 -0.070068359375 -0.15447998046875 0.5463623046874974 +0.693875 -0.07232666015625 -0.159454345703125 0.5463623046874974 +0.6940000000000001 -0.07232666015625 -0.159454345703125 0.5463623046874974 +0.694125 -0.074554443359375 -0.164398193359375 0.5463623046874974 +0.69425 -0.0767822265625 -0.169342041015625 0.5463623046874974 +0.694375 -0.0767822265625 -0.169342041015625 0.5463623046874974 +0.6945 -0.07904052734375 -0.17425537109375 0.5463623046874974 +0.694625 -0.07904052734375 -0.17425537109375 0.5463623046874974 +0.69475 -0.08123779296875 -0.17913818359375 0.5463623046874974 +0.694875 -0.083465576171875 -0.18402099609375 0.5463623046874974 +0.6950000000000001 -0.083465576171875 -0.18402099609375 0.5463623046874974 +0.695125 -0.085662841796875 -0.188873291015625 0.5463623046874974 +0.69525 -0.085662841796875 -0.188873291015625 0.5463623046874974 +0.695375 -0.087860107421875 -0.1937255859375 0.5463623046874974 +0.6955 -0.090057373046875 -0.19854736328125 0.5463623046874974 +0.695625 -0.090057373046875 -0.19854736328125 0.5463623046874974 +0.69575 -0.09222412109375 -0.203338623046875 0.5463623046874974 +0.695875 -0.09222412109375 -0.203338623046875 0.5463623046874974 +0.6960000000000001 -0.094390869140625 -0.208099365234375 0.5463623046874974 +0.696125 -0.096527099609375 -0.212860107421875 0.5463623046874974 +0.69625 -0.096527099609375 -0.212860107421875 0.5463623046874974 +0.696375 -0.09869384765625 -0.21759033203125 0.5463623046874974 +0.6965 -0.09869384765625 -0.21759033203125 0.5463623046874974 +0.696625 -0.100799560546875 -0.2222900390625 0.5463623046874974 +0.69675 -0.102935791015625 -0.226959228515625 0.5463623046874974 +0.696875 -0.102935791015625 -0.226959228515625 0.5463623046874974 +0.6970000000000001 -0.10504150390625 -0.231597900390625 0.5463623046874974 +0.697125 -0.10504150390625 -0.231597900390625 0.5463623046874974 +0.69725 -0.107147216796875 -0.236236572265625 0.5463623046874974 +0.697375 -0.109222412109375 -0.2408447265625 0.5463623046874974 +0.6975 -0.109222412109375 -0.2408447265625 0.5463623046874974 +0.6976250000000001 -0.111297607421875 -0.24542236328125 0.5463623046874974 +0.69775 -0.111297607421875 -0.24542236328125 0.5463623046874974 +0.697875 -0.113372802734375 -0.249969482421875 0.5463623046874974 +0.6980000000000001 -0.11541748046875 -0.25445556640625 0.5463623046874974 +0.698125 -0.11541748046875 -0.25445556640625 0.5463623046874974 +0.69825 -0.117462158203125 -0.25897216796875 0.5463623046874974 +0.698375 -0.117462158203125 -0.25897216796875 0.5463623046874974 +0.6985 -0.119476318359375 -0.263427734375 0.5463623046874974 +0.6986250000000001 -0.121490478515625 -0.267852783203125 0.5463623046874974 +0.69875 -0.121490478515625 -0.267852783203125 0.5463623046874974 +0.698875 -0.123504638671875 -0.27227783203125 0.5463623046874974 +0.6990000000000001 -0.123504638671875 -0.27227783203125 0.5463623046874974 +0.699125 -0.125457763671875 -0.276641845703125 0.5463623046874974 +0.69925 -0.12744140625 -0.280975341796875 0.5463623046874974 +0.699375 -0.12744140625 -0.280975341796875 0.5463623046874974 +0.6995 -0.12939453125 -0.285308837890625 0.5463623046874974 +0.6996250000000001 -0.12939453125 -0.285308837890625 0.5463623046874974 +0.69975 -0.13134765625 -0.289581298828125 0.5463623046874974 +0.699875 -0.133270263671875 -0.2938232421875 0.5463623046874974 +0.7000000000000001 -0.133270263671875 -0.2938232421875 0.5463623046874974 +0.700125 -0.13519287109375 -0.298065185546875 0.5463623046874974 +0.70025 -0.13519287109375 -0.298065185546875 0.5463623046874974 +0.700375 -0.1370849609375 -0.30224609375 0.5463623046874974 +0.7005 -0.13897705078125 -0.306396484375 0.5463623046874974 +0.7006250000000001 -0.13897705078125 -0.306396484375 0.5463623046874974 +0.70075 -0.140838623046875 -0.310516357421875 0.5463623046874974 +0.700875 -0.140838623046875 -0.310516357421875 0.5463623046874974 +0.7010000000000001 -0.1427001953125 -0.314605712890625 0.5463623046874974 +0.701125 -0.14453125 -0.31866455078125 0.5463623046874974 +0.70125 -0.14453125 -0.31866455078125 0.5463623046874974 +0.701375 -0.1463623046875 -0.322662353515625 0.5463623046874974 +0.7015 -0.1463623046875 -0.322662353515625 0.5463623046874974 +0.7016250000000001 -0.148162841796875 -0.32666015625 0.5463623046874974 +0.70175 -0.14996337890625 -0.330596923828125 0.5463623046874974 +0.701875 -0.14996337890625 -0.330596923828125 0.5463623046874974 +0.7020000000000001 -0.1517333984375 -0.334503173828125 0.5463623046874974 +0.702125 -0.1517333984375 -0.334503173828125 0.5463623046874974 +0.70225 -0.153472900390625 -0.33837890625 0.5463623046874974 +0.702375 -0.15521240234375 -0.34222412109375 0.5463623046874974 +0.7025 -0.15521240234375 -0.34222412109375 0.5463623046874974 +0.7026250000000001 -0.156951904296875 -0.34600830078125 0.5463623046874974 +0.70275 -0.156951904296875 -0.34600830078125 0.5463623046874974 +0.702875 -0.15863037109375 -0.349761962890625 0.5463623046874974 +0.7030000000000001 -0.16033935546875 -0.353485107421875 0.5463623046874974 +0.703125 -0.16033935546875 -0.353485107421875 0.5463623046874974 +0.70325 -0.162017822265625 -0.357177734375 0.5463623046874974 +0.703375 -0.162017822265625 -0.357177734375 0.5463623046874974 +0.7035 -0.163665771484375 -0.360809326171875 0.5463623046874974 +0.7036250000000001 -0.165283203125 -0.36444091796875 0.5463623046874974 +0.70375 -0.165283203125 -0.36444091796875 0.5463623046874974 +0.703875 -0.166900634765625 -0.36798095703125 0.5463623046874974 +0.704 -0.3035888671875 -0.36798095703125 0.17499999999999718 +0.704125 -0.306488037109375 -0.37152099609375 0.17499999999999718 +0.70425 -0.309356689453125 -0.375 0.17499999999999718 +0.704375 -0.309356689453125 -0.375 0.17499999999999718 +0.7045 -0.31219482421875 -0.378448486328125 0.17499999999999718 +0.7046250000000001 -0.31219482421875 -0.378448486328125 0.17499999999999718 +0.70475 -0.31500244140625 -0.3818359375 0.17499999999999718 +0.704875 -0.317779541015625 -0.38519287109375 0.17499999999999718 +0.705 -0.317779541015625 -0.38519287109375 0.17499999999999718 +0.705125 -0.32049560546875 -0.388519287109375 0.17499999999999718 +0.70525 -0.32049560546875 -0.388519287109375 0.17499999999999718 +0.705375 -0.323211669921875 -0.39178466796875 0.17499999999999718 +0.7055 -0.325897216796875 -0.39501953125 0.17499999999999718 +0.7056250000000001 -0.325897216796875 -0.39501953125 0.17499999999999718 +0.70575 -0.3284912109375 -0.398193359375 0.17499999999999718 +0.705875 -0.3284912109375 -0.398193359375 0.17499999999999718 +0.706 -0.331085205078125 -0.401336669921875 0.17499999999999718 +0.706125 -0.333648681640625 -0.404449462890625 0.17499999999999718 +0.70625 -0.333648681640625 -0.404449462890625 0.17499999999999718 +0.706375 -0.336181640625 -0.407501220703125 0.17499999999999718 +0.7065 -0.336181640625 -0.407501220703125 0.17499999999999718 +0.7066250000000001 -0.338653564453125 -0.4105224609375 0.17499999999999718 +0.70675 -0.341094970703125 -0.413482666015625 0.17499999999999718 +0.706875 -0.341094970703125 -0.413482666015625 0.17499999999999718 +0.707 -0.343505859375 -0.416412353515625 0.17499999999999718 +0.707125 -0.343505859375 -0.416412353515625 0.17499999999999718 +0.70725 -0.34588623046875 -0.419281005859375 0.17499999999999718 +0.707375 -0.348236083984375 -0.422119140625 0.17499999999999718 +0.7075 -0.348236083984375 -0.422119140625 0.17499999999999718 +0.7076250000000001 -0.35052490234375 -0.424896240234375 0.17499999999999718 +0.70775 -0.35052490234375 -0.424896240234375 0.17499999999999718 +0.707875 -0.352783203125 -0.4276123046875 0.17499999999999718 +0.708 -0.35498046875 -0.4302978515625 0.17499999999999718 +0.708125 -0.35498046875 -0.4302978515625 0.17499999999999718 +0.70825 -0.357177734375 -0.432952880859375 0.17499999999999718 +0.708375 -0.357177734375 -0.432952880859375 0.17499999999999718 +0.7085 -0.35931396484375 -0.435546875 0.17499999999999718 +0.7086250000000001 -0.36138916015625 -0.438079833984375 0.17499999999999718 +0.70875 -0.36138916015625 -0.438079833984375 0.17499999999999718 +0.708875 -0.36346435546875 -0.440582275390625 0.17499999999999718 +0.709 -0.36346435546875 -0.440582275390625 0.17499999999999718 +0.709125 -0.365509033203125 -0.44305419921875 0.17499999999999718 +0.70925 -0.367462158203125 -0.4454345703125 0.17499999999999718 +0.709375 -0.367462158203125 -0.4454345703125 0.17499999999999718 +0.7095 -0.369415283203125 -0.447784423828125 0.17499999999999718 +0.7096250000000001 -0.369415283203125 -0.447784423828125 0.17499999999999718 +0.70975 -0.371307373046875 -0.450103759765625 0.17499999999999718 +0.709875 -0.3731689453125 -0.452362060546875 0.17499999999999718 +0.71 -0.3731689453125 -0.452362060546875 0.17499999999999718 +0.710125 -0.375 -0.454559326171875 0.17499999999999718 +0.71025 -0.375 -0.454559326171875 0.17499999999999718 +0.710375 -0.37677001953125 -0.45672607421875 0.17499999999999718 +0.7105 -0.378509521484375 -0.458831787109375 0.17499999999999718 +0.7106250000000001 -0.378509521484375 -0.458831787109375 0.17499999999999718 +0.71075 -0.38018798828125 -0.46087646484375 0.17499999999999718 +0.710875 -0.38018798828125 -0.46087646484375 0.17499999999999718 +0.711 -0.381866455078125 -0.462890625 0.17499999999999718 +0.711125 -0.383453369140625 -0.464813232421875 0.17499999999999718 +0.71125 -0.383453369140625 -0.464813232421875 0.17499999999999718 +0.711375 -0.385040283203125 -0.46673583984375 0.17499999999999718 +0.7115 -0.385040283203125 -0.46673583984375 0.17499999999999718 +0.7116250000000001 -0.386566162109375 -0.46856689453125 0.17499999999999718 +0.71175 -0.388031005859375 -0.470367431640625 0.17499999999999718 +0.711875 -0.388031005859375 -0.470367431640625 0.17499999999999718 +0.712 -0.389495849609375 -0.472137451171875 0.17499999999999718 +0.712125 -0.389495849609375 -0.472137451171875 0.17499999999999718 +0.71225 -0.390869140625 -0.47381591796875 0.17499999999999718 +0.712375 -0.392242431640625 -0.4754638671875 0.17499999999999718 +0.7125 -0.392242431640625 -0.4754638671875 0.17499999999999718 +0.7126250000000001 -0.3935546875 -0.47705078125 0.17499999999999718 +0.71275 -0.3935546875 -0.47705078125 0.17499999999999718 +0.712875 -0.39483642578125 -0.478607177734375 0.17499999999999718 +0.713 -0.39605712890625 -0.480072021484375 0.17499999999999718 +0.713125 -0.39605712890625 -0.480072021484375 0.17499999999999718 +0.7132500000000001 -0.397216796875 -0.48150634765625 0.17499999999999718 +0.713375 -0.397216796875 -0.48150634765625 0.17499999999999718 +0.7135 -0.39837646484375 -0.48291015625 0.17499999999999718 +0.7136250000000001 -0.39947509765625 -0.484222412109375 0.17499999999999718 +0.71375 -0.39947509765625 -0.484222412109375 0.17499999999999718 +0.713875 -0.4005126953125 -0.485504150390625 0.17499999999999718 +0.714 -0.4005126953125 -0.485504150390625 0.17499999999999718 +0.714125 -0.401519775390625 -0.486724853515625 0.17499999999999718 +0.7142500000000001 -0.402496337890625 -0.487884521484375 0.17499999999999718 +0.714375 -0.402496337890625 -0.487884521484375 0.17499999999999718 +0.7145 -0.403411865234375 -0.489013671875 0.17499999999999718 +0.7146250000000001 -0.403411865234375 -0.489013671875 0.17499999999999718 +0.71475 -0.404296875 -0.490081787109375 0.17499999999999718 +0.714875 -0.405120849609375 -0.4910888671875 0.17499999999999718 +0.715 -0.405120849609375 -0.4910888671875 0.17499999999999718 +0.715125 -0.405914306640625 -0.492034912109375 0.17499999999999718 +0.7152500000000001 -0.405914306640625 -0.492034912109375 0.17499999999999718 +0.715375 -0.406646728515625 -0.492950439453125 0.17499999999999718 +0.7155 -0.4073486328125 -0.4937744140625 0.17499999999999718 +0.7156250000000001 -0.4073486328125 -0.4937744140625 0.17499999999999718 +0.71575 -0.407989501953125 -0.49456787109375 0.17499999999999718 +0.715875 -0.407989501953125 -0.49456787109375 0.17499999999999718 +0.716 -0.408599853515625 -0.49530029296875 0.17499999999999718 +0.716125 -0.4091796875 -0.496002197265625 0.17499999999999718 +0.7162500000000001 -0.4091796875 -0.496002197265625 0.17499999999999718 +0.716375 -0.409698486328125 -0.496612548828125 0.17499999999999718 +0.7165 -0.409698486328125 -0.496612548828125 0.17499999999999718 +0.7166250000000001 -0.41015625 -0.4971923828125 0.17499999999999718 +0.71675 -0.41058349609375 -0.497711181640625 0.17499999999999718 +0.716875 -0.41058349609375 -0.497711181640625 0.17499999999999718 +0.717 -0.410980224609375 -0.498199462890625 0.17499999999999718 +0.717125 -0.410980224609375 -0.498199462890625 0.17499999999999718 +0.7172500000000001 -0.41131591796875 -0.49859619140625 0.17499999999999718 +0.717375 -0.41162109375 -0.49896240234375 0.17499999999999718 +0.7175 -0.41162109375 -0.49896240234375 0.17499999999999718 +0.7176250000000001 -0.411865234375 -0.499267578125 0.17499999999999718 +0.71775 -0.411865234375 -0.499267578125 0.17499999999999718 +0.717875 -0.412078857421875 -0.49951171875 0.17499999999999718 +0.718 -0.4122314453125 -0.49969482421875 0.17499999999999718 +0.718125 -0.4122314453125 -0.49969482421875 0.17499999999999718 +0.7182500000000001 -0.412322998046875 -0.49981689453125 0.17499999999999718 +0.718375 -0.412322998046875 -0.49981689453125 0.17499999999999718 +0.7185 -0.41241455078125 -0.499908447265625 0.17499999999999718 +0.7186250000000001 -0.412445068359375 -0.49993896484375 0.17499999999999718 +0.71875 -0.412445068359375 -0.49993896484375 0.17499999999999718 +0.718875 -0.41241455078125 -0.499908447265625 0.17499999999999718 +0.719 -0.41241455078125 -0.499908447265625 0.17499999999999718 +0.719125 -0.412322998046875 -0.49981689453125 0.17499999999999718 +0.7192500000000001 -0.4122314453125 -0.49969482421875 0.17499999999999718 +0.719375 -0.4122314453125 -0.49969482421875 0.17499999999999718 +0.7195 -0.412078857421875 -0.49951171875 0.17499999999999718 +0.7196250000000001 -0.412078857421875 -0.49951171875 0.17499999999999718 +0.71975 -0.411865234375 -0.499267578125 0.17499999999999718 +0.719875 -0.41162109375 -0.49896240234375 0.17499999999999718 +0.72 -0.41162109375 -0.49896240234375 0.17499999999999718 +0.720125 -0.41131591796875 -0.49859619140625 0.17499999999999718 +0.7202500000000001 -0.41131591796875 -0.49859619140625 0.17499999999999718 +0.720375 -0.410980224609375 -0.498199462890625 0.17499999999999718 +0.7205 -0.41058349609375 -0.497711181640625 0.17499999999999718 +0.720625 -0.41058349609375 -0.497711181640625 0.17499999999999718 +0.72075 -0.41015625 -0.4971923828125 0.17499999999999718 +0.720875 -0.41015625 -0.4971923828125 0.17499999999999718 +0.721 -0.409698486328125 -0.496612548828125 0.17499999999999718 +0.721125 -0.4091796875 -0.496002197265625 0.17499999999999718 +0.7212500000000001 -0.4091796875 -0.496002197265625 0.17499999999999718 +0.721375 -0.408599853515625 -0.49530029296875 0.17499999999999718 +0.7215 -0.408599853515625 -0.49530029296875 0.17499999999999718 +0.721625 -0.407989501953125 -0.49456787109375 0.17499999999999718 +0.72175 -0.4073486328125 -0.4937744140625 0.17499999999999718 +0.721875 -0.4073486328125 -0.4937744140625 0.17499999999999718 +0.722 -0.406646728515625 -0.492950439453125 0.17499999999999718 +0.722125 -0.406646728515625 -0.492950439453125 0.17499999999999718 +0.7222500000000001 -0.405914306640625 -0.492034912109375 0.17499999999999718 +0.722375 -0.405120849609375 -0.4910888671875 0.17499999999999718 +0.7225 -0.405120849609375 -0.4910888671875 0.17499999999999718 +0.722625 -0.404296875 -0.490081787109375 0.17499999999999718 +0.72275 -0.404296875 -0.490081787109375 0.17499999999999718 +0.722875 -0.403411865234375 -0.489013671875 0.17499999999999718 +0.723 -0.402496337890625 -0.487884521484375 0.17499999999999718 +0.723125 -0.402496337890625 -0.487884521484375 0.17499999999999718 +0.7232500000000001 -0.401519775390625 -0.486724853515625 0.17499999999999718 +0.723375 -0.401519775390625 -0.486724853515625 0.17499999999999718 +0.7235 -0.4005126953125 -0.485504150390625 0.17499999999999718 +0.723625 -0.39947509765625 -0.484222412109375 0.17499999999999718 +0.72375 -0.39947509765625 -0.484222412109375 0.17499999999999718 +0.723875 -0.39837646484375 -0.48291015625 0.17499999999999718 +0.724 -0.39837646484375 -0.48291015625 0.17499999999999718 +0.724125 -0.397216796875 -0.48150634765625 0.17499999999999718 +0.7242500000000001 -0.39605712890625 -0.480072021484375 0.17499999999999718 +0.724375 -0.39605712890625 -0.480072021484375 0.17499999999999718 +0.7245 -0.39483642578125 -0.478607177734375 0.17499999999999718 +0.724625 -0.39483642578125 -0.478607177734375 0.17499999999999718 +0.72475 -0.3935546875 -0.47705078125 0.17499999999999718 +0.724875 -0.392242431640625 -0.4754638671875 0.17499999999999718 +0.725 -0.392242431640625 -0.4754638671875 0.17499999999999718 +0.725125 -0.390869140625 -0.47381591796875 0.17499999999999718 +0.7252500000000001 -0.390869140625 -0.47381591796875 0.17499999999999718 +0.725375 -0.389495849609375 -0.472137451171875 0.17499999999999718 +0.7255 -0.388031005859375 -0.470367431640625 0.17499999999999718 +0.725625 -0.388031005859375 -0.470367431640625 0.17499999999999718 +0.72575 -0.386566162109375 -0.46856689453125 0.17499999999999718 +0.725875 -0.386566162109375 -0.46856689453125 0.17499999999999718 +0.726 -0.385040283203125 -0.46673583984375 0.17499999999999718 +0.726125 -0.383453369140625 -0.464813232421875 0.17499999999999718 +0.7262500000000001 -0.383453369140625 -0.464813232421875 0.17499999999999718 +0.726375 -0.381866455078125 -0.462890625 0.17499999999999718 +0.7265 -0.381866455078125 -0.462890625 0.17499999999999718 +0.726625 -0.38018798828125 -0.46087646484375 0.17499999999999718 +0.72675 -0.378509521484375 -0.458831787109375 0.17499999999999718 +0.726875 -0.378509521484375 -0.458831787109375 0.17499999999999718 +0.727 -0.37677001953125 -0.45672607421875 0.17499999999999718 +0.727125 -0.37677001953125 -0.45672607421875 0.17499999999999718 +0.7272500000000001 -0.375 -0.454559326171875 0.17499999999999718 +0.727375 -0.3731689453125 -0.452362060546875 0.17499999999999718 +0.7275 -0.3731689453125 -0.452362060546875 0.17499999999999718 +0.727625 -0.371307373046875 -0.450103759765625 0.17499999999999718 +0.72775 -0.371307373046875 -0.450103759765625 0.17499999999999718 +0.727875 -0.369415283203125 -0.447784423828125 0.17499999999999718 +0.728 -0.367462158203125 -0.4454345703125 0.17499999999999718 +0.728125 -0.367462158203125 -0.4454345703125 0.17499999999999718 +0.7282500000000001 -0.365509033203125 -0.44305419921875 0.17499999999999718 +0.728375 -0.365509033203125 -0.44305419921875 0.17499999999999718 +0.7285 -0.36346435546875 -0.440582275390625 0.17499999999999718 +0.728625 -0.36138916015625 -0.438079833984375 0.17499999999999718 +0.72875 -0.36138916015625 -0.438079833984375 0.17499999999999718 +0.7288750000000001 -0.35931396484375 -0.435546875 0.17499999999999718 +0.729 -0.35931396484375 -0.435546875 0.17499999999999718 +0.729125 -0.357177734375 -0.432952880859375 0.17499999999999718 +0.7292500000000001 -0.35498046875 -0.4302978515625 0.17499999999999718 +0.729375 -0.35498046875 -0.4302978515625 0.17499999999999718 +0.7295 -0.352783203125 -0.4276123046875 0.17499999999999718 +0.729625 -0.352783203125 -0.4276123046875 0.17499999999999718 +0.72975 -0.35052490234375 -0.424896240234375 0.17499999999999718 +0.7298750000000001 -0.348236083984375 -0.422119140625 0.17499999999999718 +0.73 -0.348236083984375 -0.422119140625 0.17499999999999718 +0.730125 -0.34588623046875 -0.419281005859375 0.17499999999999718 +0.7302500000000001 -0.34588623046875 -0.419281005859375 0.17499999999999718 +0.730375 -0.343505859375 -0.416412353515625 0.17499999999999718 +0.7305 -0.341094970703125 -0.413482666015625 0.17499999999999718 +0.730625 -0.341094970703125 -0.413482666015625 0.17499999999999718 +0.73075 -0.338653564453125 -0.4105224609375 0.17499999999999718 +0.7308750000000001 -0.338653564453125 -0.4105224609375 0.17499999999999718 +0.731 -0.336181640625 -0.407501220703125 0.17499999999999718 +0.731125 -0.333648681640625 -0.404449462890625 0.17499999999999718 +0.7312500000000001 -0.333648681640625 -0.404449462890625 0.17499999999999718 +0.731375 -0.331085205078125 -0.401336669921875 0.17499999999999718 +0.7315 -0.331085205078125 -0.401336669921875 0.17499999999999718 +0.731625 -0.3284912109375 -0.398193359375 0.17499999999999718 +0.73175 -0.325897216796875 -0.39501953125 0.17499999999999718 +0.7318750000000001 -0.325897216796875 -0.39501953125 0.17499999999999718 +0.732 -0.323211669921875 -0.39178466796875 0.17499999999999718 +0.732125 -0.323211669921875 -0.39178466796875 0.17499999999999718 +0.7322500000000001 -0.32049560546875 -0.388519287109375 0.17499999999999718 +0.732375 -0.317779541015625 -0.38519287109375 0.17499999999999718 +0.7325 -0.317779541015625 -0.38519287109375 0.17499999999999718 +0.732625 -0.31500244140625 -0.3818359375 0.17499999999999718 +0.73275 -0.31500244140625 -0.3818359375 0.17499999999999718 +0.7328750000000001 -0.31219482421875 -0.378448486328125 0.17499999999999718 +0.733 -0.309356689453125 -0.375 0.17499999999999718 +0.733125 -0.309356689453125 -0.375 0.17499999999999718 +0.7332500000000001 -0.306488037109375 -0.37152099609375 0.17499999999999718 +0.733375 -0.306488037109375 -0.37152099609375 0.17499999999999718 +0.7335 -0.3035888671875 -0.36798095703125 0.17499999999999718 +0.733625 -0.3006591796875 -0.36444091796875 0.17499999999999718 +0.73375 -0.3006591796875 -0.36444091796875 0.17499999999999718 +0.7338750000000001 -0.29766845703125 -0.360809326171875 0.17499999999999718 +0.734 -0.29766845703125 -0.360809326171875 0.17499999999999718 +0.734125 -0.294677734375 -0.357177734375 0.17499999999999718 +0.7342500000000001 -0.2916259765625 -0.353485107421875 0.17499999999999718 +0.734375 -0.2916259765625 -0.353485107421875 0.17499999999999718 +0.7345 -0.288543701171875 -0.349761962890625 0.17499999999999718 +0.734625 -0.288543701171875 -0.349761962890625 0.17499999999999718 +0.73475 -0.28546142578125 -0.34600830078125 0.17499999999999718 +0.7348750000000001 -0.282318115234375 -0.34222412109375 0.17499999999999718 +0.735 -0.282318115234375 -0.34222412109375 0.17499999999999718 +0.735125 -0.279144287109375 -0.33837890625 0.17499999999999718 +0.7352500000000001 -0.279144287109375 -0.33837890625 0.17499999999999718 +0.735375 -0.275970458984375 -0.334503173828125 0.17499999999999718 +0.7355 -0.272735595703125 -0.330596923828125 0.17499999999999718 +0.735625 -0.272735595703125 -0.330596923828125 0.17499999999999718 +0.73575 -0.26947021484375 -0.32666015625 0.17499999999999718 +0.7358750000000001 -0.26947021484375 -0.32666015625 0.17499999999999718 +0.736 -0.322662353515625 -0.25030517578125 -0.224243164062503132 +0.736125 -0.31866455078125 -0.2471923828125 -0.224243164062503132 +0.73625 -0.31866455078125 -0.2471923828125 -0.224243164062503132 +0.736375 -0.314605712890625 -0.244049072265625 -0.224243164062503132 +0.7365 -0.314605712890625 -0.244049072265625 -0.224243164062503132 +0.736625 -0.310516357421875 -0.240875244140625 -0.224243164062503132 +0.73675 -0.306396484375 -0.2376708984375 -0.224243164062503132 +0.7368750000000001 -0.306396484375 -0.2376708984375 -0.224243164062503132 +0.737 -0.30224609375 -0.23443603515625 -0.224243164062503132 +0.737125 -0.30224609375 -0.23443603515625 -0.224243164062503132 +0.73725 -0.298065185546875 -0.231201171875 -0.224243164062503132 +0.737375 -0.2938232421875 -0.227935791015625 -0.224243164062503132 +0.7375 -0.2938232421875 -0.227935791015625 -0.224243164062503132 +0.737625 -0.289581298828125 -0.224639892578125 -0.224243164062503132 +0.73775 -0.289581298828125 -0.224639892578125 -0.224243164062503132 +0.7378750000000001 -0.285308837890625 -0.2213134765625 -0.224243164062503132 +0.738 -0.280975341796875 -0.21795654296875 -0.224243164062503132 +0.738125 -0.280975341796875 -0.21795654296875 -0.224243164062503132 +0.73825 -0.276641845703125 -0.214599609375 -0.224243164062503132 +0.738375 -0.276641845703125 -0.214599609375 -0.224243164062503132 +0.7385 -0.27227783203125 -0.211212158203125 -0.224243164062503132 +0.738625 -0.267852783203125 -0.207794189453125 -0.224243164062503132 +0.73875 -0.267852783203125 -0.207794189453125 -0.224243164062503132 +0.7388750000000001 -0.263427734375 -0.204345703125 -0.224243164062503132 +0.739 -0.263427734375 -0.204345703125 -0.224243164062503132 +0.739125 -0.25897216796875 -0.200897216796875 -0.224243164062503132 +0.73925 -0.25445556640625 -0.1973876953125 -0.224243164062503132 +0.739375 -0.25445556640625 -0.1973876953125 -0.224243164062503132 +0.7395 -0.249969482421875 -0.193878173828125 -0.224243164062503132 +0.739625 -0.249969482421875 -0.193878173828125 -0.224243164062503132 +0.73975 -0.24542236328125 -0.19036865234375 -0.224243164062503132 +0.7398750000000001 -0.2408447265625 -0.186798095703125 -0.224243164062503132 +0.74 -0.2408447265625 -0.186798095703125 -0.224243164062503132 +0.740125 -0.236236572265625 -0.183258056640625 -0.224243164062503132 +0.74025 -0.236236572265625 -0.183258056640625 -0.224243164062503132 +0.740375 -0.231597900390625 -0.179656982421875 -0.224243164062503132 +0.7405 -0.226959228515625 -0.176055908203125 -0.224243164062503132 +0.740625 -0.226959228515625 -0.176055908203125 -0.224243164062503132 +0.74075 -0.2222900390625 -0.17242431640625 -0.224243164062503132 +0.7408750000000001 -0.2222900390625 -0.17242431640625 -0.224243164062503132 +0.741 -0.21759033203125 -0.16876220703125 -0.224243164062503132 +0.741125 -0.212860107421875 -0.16510009765625 -0.224243164062503132 +0.74125 -0.212860107421875 -0.16510009765625 -0.224243164062503132 +0.741375 -0.208099365234375 -0.16143798828125 -0.224243164062503132 +0.7415 -0.208099365234375 -0.16143798828125 -0.224243164062503132 +0.741625 -0.203338623046875 -0.15771484375 -0.224243164062503132 +0.74175 -0.19854736328125 -0.15399169921875 -0.224243164062503132 +0.7418750000000001 -0.19854736328125 -0.15399169921875 -0.224243164062503132 +0.742 -0.1937255859375 -0.1502685546875 -0.224243164062503132 +0.742125 -0.1937255859375 -0.1502685546875 -0.224243164062503132 +0.74225 -0.188873291015625 -0.146514892578125 -0.224243164062503132 +0.742375 -0.18402099609375 -0.14276123046875 -0.224243164062503132 +0.7425 -0.18402099609375 -0.14276123046875 -0.224243164062503132 +0.742625 -0.17913818359375 -0.138946533203125 -0.224243164062503132 +0.74275 -0.17913818359375 -0.138946533203125 -0.224243164062503132 +0.7428750000000001 -0.17425537109375 -0.135162353515625 -0.224243164062503132 +0.743 -0.169342041015625 -0.13134765625 -0.224243164062503132 +0.743125 -0.169342041015625 -0.13134765625 -0.224243164062503132 +0.74325 -0.164398193359375 -0.12750244140625 -0.224243164062503132 +0.743375 -0.164398193359375 -0.12750244140625 -0.224243164062503132 +0.7435 -0.159454345703125 -0.123687744140625 -0.224243164062503132 +0.743625 -0.15447998046875 -0.11981201171875 -0.224243164062503132 +0.74375 -0.15447998046875 -0.11981201171875 -0.224243164062503132 +0.7438750000000001 -0.14947509765625 -0.115936279296875 -0.224243164062503132 +0.744 -0.14947509765625 -0.115936279296875 -0.224243164062503132 +0.744125 -0.14447021484375 -0.112060546875 -0.224243164062503132 +0.74425 -0.13946533203125 -0.108184814453125 -0.224243164062503132 +0.744375 -0.13946533203125 -0.108184814453125 -0.224243164062503132 +0.7445000000000001 -0.134429931640625 -0.104278564453125 -0.224243164062503132 +0.744625 -0.134429931640625 -0.104278564453125 -0.224243164062503132 +0.74475 -0.129364013671875 -0.100341796875 -0.224243164062503132 +0.7448750000000001 -0.124298095703125 -0.096435546875 -0.224243164062503132 +0.745 -0.124298095703125 -0.096435546875 -0.224243164062503132 +0.745125 -0.119232177734375 -0.092498779296875 -0.224243164062503132 +0.74525 -0.119232177734375 -0.092498779296875 -0.224243164062503132 +0.745375 -0.1141357421875 -0.088531494140625 -0.224243164062503132 +0.7455000000000001 -0.109039306640625 -0.084564208984375 -0.224243164062503132 +0.745625 -0.109039306640625 -0.084564208984375 -0.224243164062503132 +0.74575 -0.103912353515625 -0.080596923828125 -0.224243164062503132 +0.7458750000000001 -0.103912353515625 -0.080596923828125 -0.224243164062503132 +0.746 -0.098785400390625 -0.076629638671875 -0.224243164062503132 +0.746125 -0.093658447265625 -0.0726318359375 -0.224243164062503132 +0.74625 -0.093658447265625 -0.0726318359375 -0.224243164062503132 +0.746375 -0.0885009765625 -0.06866455078125 -0.224243164062503132 +0.7465000000000001 -0.0885009765625 -0.06866455078125 -0.224243164062503132 +0.746625 -0.083343505859375 -0.064666748046875 -0.224243164062503132 +0.74675 -0.07818603515625 -0.060638427734375 -0.224243164062503132 +0.7468750000000001 -0.07818603515625 -0.060638427734375 -0.224243164062503132 +0.747 -0.072998046875 -0.056640625 -0.224243164062503132 +0.747125 -0.072998046875 -0.056640625 -0.224243164062503132 +0.74725 -0.06781005859375 -0.0526123046875 -0.224243164062503132 +0.747375 -0.0626220703125 -0.048583984375 -0.224243164062503132 +0.7475000000000001 -0.0626220703125 -0.048583984375 -0.224243164062503132 +0.747625 -0.05743408203125 -0.0445556640625 -0.224243164062503132 +0.74775 -0.05743408203125 -0.0445556640625 -0.224243164062503132 +0.7478750000000001 -0.05224609375 -0.04052734375 -0.224243164062503132 +0.748 -0.047027587890625 -0.036468505859375 -0.224243164062503132 +0.748125 -0.047027587890625 -0.036468505859375 -0.224243164062503132 +0.74825 -0.04180908203125 -0.032440185546875 -0.224243164062503132 +0.748375 -0.04180908203125 -0.032440185546875 -0.224243164062503132 +0.7485000000000001 -0.036590576171875 -0.02838134765625 -0.224243164062503132 +0.748625 -0.0313720703125 -0.024322509765625 -0.224243164062503132 +0.74875 -0.0313720703125 -0.024322509765625 -0.224243164062503132 +0.7488750000000001 -0.026123046875 -0.020263671875 -0.224243164062503132 +0.749 -0.026123046875 -0.020263671875 -0.224243164062503132 +0.749125 -0.020904541015625 -0.0162353515625 -0.224243164062503132 +0.74925 -0.01568603515625 -0.012176513671875 -0.224243164062503132 +0.749375 -0.01568603515625 -0.012176513671875 -0.224243164062503132 +0.7495000000000001 -0.01043701171875 -0.00811767578125 -0.224243164062503132 +0.749625 -0.01043701171875 -0.00811767578125 -0.224243164062503132 +0.74975 -0.005218505859375 -0.004058837890625 -0.224243164062503132 +0.7498750000000001 0.0 0.0 -0.224243164062503132 +0.75 0.0 0.0 -0.224243164062503132 +0.750125 0.005218505859375 0.004058837890625 -0.224243164062503132 +0.75025 0.005218505859375 0.004058837890625 -0.224243164062503132 +0.750375 0.01043701171875 0.00811767578125 -0.224243164062503132 +0.7505000000000001 0.01568603515625 0.012176513671875 -0.224243164062503132 +0.750625 0.01568603515625 0.012176513671875 -0.224243164062503132 +0.75075 0.020904541015625 0.0162353515625 -0.224243164062503132 +0.7508750000000001 0.020904541015625 0.0162353515625 -0.224243164062503132 +0.751 0.026123046875 0.020263671875 -0.224243164062503132 +0.751125 0.0313720703125 0.024322509765625 -0.224243164062503132 +0.75125 0.0313720703125 0.024322509765625 -0.224243164062503132 +0.751375 0.036590576171875 0.02838134765625 -0.224243164062503132 +0.7515000000000001 0.036590576171875 0.02838134765625 -0.224243164062503132 +0.751625 0.04180908203125 0.032440185546875 -0.224243164062503132 +0.75175 0.047027587890625 0.036468505859375 -0.224243164062503132 +0.7518750000000001 0.047027587890625 0.036468505859375 -0.224243164062503132 +0.752 0.05224609375 0.04052734375 -0.224243164062503132 +0.752125 0.05224609375 0.04052734375 -0.224243164062503132 +0.75225 0.05743408203125 0.0445556640625 -0.224243164062503132 +0.752375 0.0626220703125 0.048583984375 -0.224243164062503132 +0.7525000000000001 0.0626220703125 0.048583984375 -0.224243164062503132 +0.752625 0.06781005859375 0.0526123046875 -0.224243164062503132 +0.75275 0.06781005859375 0.0526123046875 -0.224243164062503132 +0.752875 0.072998046875 0.056640625 -0.224243164062503132 +0.753 0.07818603515625 0.060638427734375 -0.224243164062503132 +0.753125 0.07818603515625 0.060638427734375 -0.224243164062503132 +0.75325 0.083343505859375 0.064666748046875 -0.224243164062503132 +0.753375 0.083343505859375 0.064666748046875 -0.224243164062503132 +0.7535000000000001 0.0885009765625 0.06866455078125 -0.224243164062503132 +0.753625 0.093658447265625 0.0726318359375 -0.224243164062503132 +0.75375 0.093658447265625 0.0726318359375 -0.224243164062503132 +0.753875 0.098785400390625 0.076629638671875 -0.224243164062503132 +0.754 0.098785400390625 0.076629638671875 -0.224243164062503132 +0.754125 0.103912353515625 0.080596923828125 -0.224243164062503132 +0.75425 0.109039306640625 0.084564208984375 -0.224243164062503132 +0.754375 0.109039306640625 0.084564208984375 -0.224243164062503132 +0.7545000000000001 0.1141357421875 0.088531494140625 -0.224243164062503132 +0.754625 0.1141357421875 0.088531494140625 -0.224243164062503132 +0.75475 0.119232177734375 0.092498779296875 -0.224243164062503132 +0.754875 0.124298095703125 0.096435546875 -0.224243164062503132 +0.755 0.124298095703125 0.096435546875 -0.224243164062503132 +0.755125 0.129364013671875 0.100341796875 -0.224243164062503132 +0.75525 0.129364013671875 0.100341796875 -0.224243164062503132 +0.755375 0.134429931640625 0.104278564453125 -0.224243164062503132 +0.7555000000000001 0.13946533203125 0.108184814453125 -0.224243164062503132 +0.755625 0.13946533203125 0.108184814453125 -0.224243164062503132 +0.75575 0.14447021484375 0.112060546875 -0.224243164062503132 +0.755875 0.14447021484375 0.112060546875 -0.224243164062503132 +0.756 0.14947509765625 0.115936279296875 -0.224243164062503132 +0.756125 0.15447998046875 0.11981201171875 -0.224243164062503132 +0.75625 0.15447998046875 0.11981201171875 -0.224243164062503132 +0.756375 0.159454345703125 0.123687744140625 -0.224243164062503132 +0.7565000000000001 0.159454345703125 0.123687744140625 -0.224243164062503132 +0.756625 0.164398193359375 0.12750244140625 -0.224243164062503132 +0.75675 0.169342041015625 0.13134765625 -0.224243164062503132 +0.756875 0.169342041015625 0.13134765625 -0.224243164062503132 +0.757 0.17425537109375 0.135162353515625 -0.224243164062503132 +0.757125 0.17425537109375 0.135162353515625 -0.224243164062503132 +0.75725 0.17913818359375 0.138946533203125 -0.224243164062503132 +0.757375 0.18402099609375 0.14276123046875 -0.224243164062503132 +0.7575000000000001 0.18402099609375 0.14276123046875 -0.224243164062503132 +0.757625 0.188873291015625 0.146514892578125 -0.224243164062503132 +0.75775 0.188873291015625 0.146514892578125 -0.224243164062503132 +0.757875 0.1937255859375 0.1502685546875 -0.224243164062503132 +0.758 0.19854736328125 0.15399169921875 -0.224243164062503132 +0.758125 0.19854736328125 0.15399169921875 -0.224243164062503132 +0.75825 0.203338623046875 0.15771484375 -0.224243164062503132 +0.758375 0.203338623046875 0.15771484375 -0.224243164062503132 +0.7585000000000001 0.208099365234375 0.16143798828125 -0.224243164062503132 +0.758625 0.212860107421875 0.16510009765625 -0.224243164062503132 +0.75875 0.212860107421875 0.16510009765625 -0.224243164062503132 +0.758875 0.21759033203125 0.16876220703125 -0.224243164062503132 +0.759 0.21759033203125 0.16876220703125 -0.224243164062503132 +0.759125 0.2222900390625 0.17242431640625 -0.224243164062503132 +0.75925 0.226959228515625 0.176055908203125 -0.224243164062503132 +0.759375 0.226959228515625 0.176055908203125 -0.224243164062503132 +0.7595000000000001 0.231597900390625 0.179656982421875 -0.224243164062503132 +0.759625 0.231597900390625 0.179656982421875 -0.224243164062503132 +0.75975 0.236236572265625 0.183258056640625 -0.224243164062503132 +0.759875 0.2408447265625 0.186798095703125 -0.224243164062503132 +0.76 0.2408447265625 0.186798095703125 -0.224243164062503132 +0.7601250000000001 0.24542236328125 0.19036865234375 -0.224243164062503132 +0.76025 0.24542236328125 0.19036865234375 -0.224243164062503132 +0.760375 0.249969482421875 0.193878173828125 -0.224243164062503132 +0.7605000000000001 0.25445556640625 0.1973876953125 -0.224243164062503132 +0.760625 0.25445556640625 0.1973876953125 -0.224243164062503132 +0.76075 0.25897216796875 0.200897216796875 -0.224243164062503132 +0.760875 0.25897216796875 0.200897216796875 -0.224243164062503132 +0.761 0.263427734375 0.204345703125 -0.224243164062503132 +0.7611250000000001 0.267852783203125 0.207794189453125 -0.224243164062503132 +0.76125 0.267852783203125 0.207794189453125 -0.224243164062503132 +0.761375 0.27227783203125 0.211212158203125 -0.224243164062503132 +0.7615000000000001 0.27227783203125 0.211212158203125 -0.224243164062503132 +0.761625 0.276641845703125 0.214599609375 -0.224243164062503132 +0.76175 0.280975341796875 0.21795654296875 -0.224243164062503132 +0.761875 0.280975341796875 0.21795654296875 -0.224243164062503132 +0.762 0.285308837890625 0.2213134765625 -0.224243164062503132 +0.7621250000000001 0.285308837890625 0.2213134765625 -0.224243164062503132 +0.76225 0.289581298828125 0.224639892578125 -0.224243164062503132 +0.762375 0.2938232421875 0.227935791015625 -0.224243164062503132 +0.7625000000000001 0.2938232421875 0.227935791015625 -0.224243164062503132 +0.762625 0.298065185546875 0.231201171875 -0.224243164062503132 +0.76275 0.298065185546875 0.231201171875 -0.224243164062503132 +0.762875 0.30224609375 0.23443603515625 -0.224243164062503132 +0.763 0.306396484375 0.2376708984375 -0.224243164062503132 +0.7631250000000001 0.306396484375 0.2376708984375 -0.224243164062503132 +0.76325 0.310516357421875 0.240875244140625 -0.224243164062503132 +0.763375 0.310516357421875 0.240875244140625 -0.224243164062503132 +0.7635000000000001 0.314605712890625 0.244049072265625 -0.224243164062503132 +0.763625 0.31866455078125 0.2471923828125 -0.224243164062503132 +0.76375 0.31866455078125 0.2471923828125 -0.224243164062503132 +0.763875 0.322662353515625 0.25030517578125 -0.224243164062503132 +0.764 0.322662353515625 0.25030517578125 -0.224243164062503132 +0.7641250000000001 0.32666015625 0.253387451171875 -0.224243164062503132 +0.76425 0.330596923828125 0.256439208984375 -0.224243164062503132 +0.764375 0.330596923828125 0.256439208984375 -0.224243164062503132 +0.7645000000000001 0.334503173828125 0.259490966796875 -0.224243164062503132 +0.764625 0.334503173828125 0.259490966796875 -0.224243164062503132 +0.76475 0.33837890625 0.262481689453125 -0.224243164062503132 +0.764875 0.34222412109375 0.265472412109375 -0.224243164062503132 +0.765 0.34222412109375 0.265472412109375 -0.224243164062503132 +0.7651250000000001 0.34600830078125 0.268402099609375 -0.224243164062503132 +0.76525 0.34600830078125 0.268402099609375 -0.224243164062503132 +0.765375 0.349761962890625 0.271331787109375 -0.224243164062503132 +0.7655000000000001 0.353485107421875 0.274200439453125 -0.224243164062503132 +0.765625 0.353485107421875 0.274200439453125 -0.224243164062503132 +0.76575 0.357177734375 0.277069091796875 -0.224243164062503132 +0.765875 0.357177734375 0.277069091796875 -0.224243164062503132 +0.766 0.360809326171875 0.279876708984375 -0.224243164062503132 +0.7661250000000001 0.36444091796875 0.282684326171875 -0.224243164062503132 +0.76625 0.36444091796875 0.282684326171875 -0.224243164062503132 +0.766375 0.36798095703125 0.28546142578125 -0.224243164062503132 +0.7665000000000001 0.36798095703125 0.28546142578125 -0.224243164062503132 +0.766625 0.37152099609375 0.288177490234375 -0.224243164062503132 +0.76675 0.375 0.2908935546875 -0.224243164062503132 +0.766875 0.375 0.2908935546875 -0.224243164062503132 +0.767 0.378448486328125 0.293548583984375 -0.224243164062503132 +0.7671250000000001 0.378448486328125 0.293548583984375 -0.224243164062503132 +0.76725 0.3818359375 0.29620361328125 -0.224243164062503132 +0.767375 0.38519287109375 0.298797607421875 -0.224243164062503132 +0.7675000000000001 0.38519287109375 0.298797607421875 -0.224243164062503132 +0.767625 0.388519287109375 0.301361083984375 -0.224243164062503132 +0.76775 0.388519287109375 0.301361083984375 -0.224243164062503132 +0.767875 0.39178466796875 0.303924560546875 -0.224243164062503132 +0.768 0.39501953125 0.162811279296875 -0.5877380371093779 +0.7681250000000001 0.39501953125 0.162811279296875 -0.5877380371093779 +0.76825 0.398193359375 0.16412353515625 -0.5877380371093779 +0.768375 0.398193359375 0.16412353515625 -0.5877380371093779 +0.7685 0.401336669921875 0.165435791015625 -0.5877380371093779 +0.768625 0.404449462890625 0.166717529296875 -0.5877380371093779 +0.76875 0.404449462890625 0.166717529296875 -0.5877380371093779 +0.768875 0.407501220703125 0.16796875 -0.5877380371093779 +0.769 0.407501220703125 0.16796875 -0.5877380371093779 +0.7691250000000001 0.4105224609375 0.169189453125 -0.5877380371093779 +0.76925 0.413482666015625 0.17041015625 -0.5877380371093779 +0.769375 0.413482666015625 0.17041015625 -0.5877380371093779 +0.7695 0.416412353515625 0.171630859375 -0.5877380371093779 +0.769625 0.416412353515625 0.171630859375 -0.5877380371093779 +0.76975 0.419281005859375 0.172821044921875 -0.5877380371093779 +0.769875 0.422119140625 0.173980712890625 -0.5877380371093779 +0.77 0.422119140625 0.173980712890625 -0.5877380371093779 +0.7701250000000001 0.424896240234375 0.175140380859375 -0.5877380371093779 +0.77025 0.424896240234375 0.175140380859375 -0.5877380371093779 +0.770375 0.4276123046875 0.176239013671875 -0.5877380371093779 +0.7705 0.4302978515625 0.1773681640625 -0.5877380371093779 +0.770625 0.4302978515625 0.1773681640625 -0.5877380371093779 +0.77075 0.432952880859375 0.178466796875 -0.5877380371093779 +0.770875 0.432952880859375 0.178466796875 -0.5877380371093779 +0.771 0.435546875 0.179534912109375 -0.5877380371093779 +0.7711250000000001 0.438079833984375 0.180572509765625 -0.5877380371093779 +0.77125 0.438079833984375 0.180572509765625 -0.5877380371093779 +0.771375 0.440582275390625 0.181610107421875 -0.5877380371093779 +0.7715 0.440582275390625 0.181610107421875 -0.5877380371093779 +0.771625 0.44305419921875 0.1826171875 -0.5877380371093779 +0.77175 0.4454345703125 0.18359375 -0.5877380371093779 +0.771875 0.4454345703125 0.18359375 -0.5877380371093779 +0.772 0.447784423828125 0.1845703125 -0.5877380371093779 +0.7721250000000001 0.447784423828125 0.1845703125 -0.5877380371093779 +0.77225 0.450103759765625 0.185516357421875 -0.5877380371093779 +0.772375 0.452362060546875 0.18646240234375 -0.5877380371093779 +0.7725 0.452362060546875 0.18646240234375 -0.5877380371093779 +0.772625 0.454559326171875 0.187347412109375 -0.5877380371093779 +0.77275 0.454559326171875 0.187347412109375 -0.5877380371093779 +0.772875 0.45672607421875 0.188232421875 -0.5877380371093779 +0.773 0.458831787109375 0.189117431640625 -0.5877380371093779 +0.7731250000000001 0.458831787109375 0.189117431640625 -0.5877380371093779 +0.77325 0.46087646484375 0.189971923828125 -0.5877380371093779 +0.773375 0.46087646484375 0.189971923828125 -0.5877380371093779 +0.7735 0.462890625 0.1907958984375 -0.5877380371093779 +0.773625 0.464813232421875 0.19158935546875 -0.5877380371093779 +0.77375 0.464813232421875 0.19158935546875 -0.5877380371093779 +0.773875 0.46673583984375 0.1923828125 -0.5877380371093779 +0.774 0.46673583984375 0.1923828125 -0.5877380371093779 +0.7741250000000001 0.46856689453125 0.193145751953125 -0.5877380371093779 +0.77425 0.470367431640625 0.193878173828125 -0.5877380371093779 +0.774375 0.470367431640625 0.193878173828125 -0.5877380371093779 +0.7745 0.472137451171875 0.194610595703125 -0.5877380371093779 +0.774625 0.472137451171875 0.194610595703125 -0.5877380371093779 +0.77475 0.47381591796875 0.195281982421875 -0.5877380371093779 +0.774875 0.4754638671875 0.19598388671875 -0.5877380371093779 +0.775 0.4754638671875 0.19598388671875 -0.5877380371093779 +0.7751250000000001 0.47705078125 0.196624755859375 -0.5877380371093779 +0.77525 0.47705078125 0.196624755859375 -0.5877380371093779 +0.775375 0.478607177734375 0.197265625 -0.5877380371093779 +0.7755 0.480072021484375 0.1978759765625 -0.5877380371093779 +0.775625 0.480072021484375 0.1978759765625 -0.5877380371093779 +0.7757500000000001 0.48150634765625 0.198455810546875 -0.5877380371093779 +0.775875 0.48150634765625 0.198455810546875 -0.5877380371093779 +0.776 0.48291015625 0.19903564453125 -0.5877380371093779 +0.7761250000000001 0.484222412109375 0.1995849609375 -0.5877380371093779 +0.77625 0.484222412109375 0.1995849609375 -0.5877380371093779 +0.776375 0.485504150390625 0.200103759765625 -0.5877380371093779 +0.7765 0.485504150390625 0.200103759765625 -0.5877380371093779 +0.776625 0.486724853515625 0.20062255859375 -0.5877380371093779 +0.7767500000000001 0.487884521484375 0.201080322265625 -0.5877380371093779 +0.776875 0.487884521484375 0.201080322265625 -0.5877380371093779 +0.777 0.489013671875 0.201568603515625 -0.5877380371093779 +0.7771250000000001 0.489013671875 0.201568603515625 -0.5877380371093779 +0.77725 0.490081787109375 0.201995849609375 -0.5877380371093779 +0.777375 0.4910888671875 0.202423095703125 -0.5877380371093779 +0.7775 0.4910888671875 0.202423095703125 -0.5877380371093779 +0.777625 0.492034912109375 0.20281982421875 -0.5877380371093779 +0.7777500000000001 0.492034912109375 0.20281982421875 -0.5877380371093779 +0.777875 0.492950439453125 0.20318603515625 -0.5877380371093779 +0.778 0.4937744140625 0.203521728515625 -0.5877380371093779 +0.7781250000000001 0.4937744140625 0.203521728515625 -0.5877380371093779 +0.77825 0.49456787109375 0.203857421875 -0.5877380371093779 +0.778375 0.49456787109375 0.203857421875 -0.5877380371093779 +0.7785 0.49530029296875 0.20416259765625 -0.5877380371093779 +0.778625 0.496002197265625 0.204437255859375 -0.5877380371093779 +0.7787500000000001 0.496002197265625 0.204437255859375 -0.5877380371093779 +0.778875 0.496612548828125 0.204681396484375 -0.5877380371093779 +0.779 0.496612548828125 0.204681396484375 -0.5877380371093779 +0.7791250000000001 0.4971923828125 0.204925537109375 -0.5877380371093779 +0.77925 0.497711181640625 0.20513916015625 -0.5877380371093779 +0.779375 0.497711181640625 0.20513916015625 -0.5877380371093779 +0.7795 0.498199462890625 0.205352783203125 -0.5877380371093779 +0.779625 0.498199462890625 0.205352783203125 -0.5877380371093779 +0.7797500000000001 0.49859619140625 0.20550537109375 -0.5877380371093779 +0.779875 0.49896240234375 0.205657958984375 -0.5877380371093779 +0.78 0.49896240234375 0.205657958984375 -0.5877380371093779 +0.7801250000000001 0.499267578125 0.205780029296875 -0.5877380371093779 +0.78025 0.499267578125 0.205780029296875 -0.5877380371093779 +0.780375 0.49951171875 0.20587158203125 -0.5877380371093779 +0.7805 0.49969482421875 0.205963134765625 -0.5877380371093779 +0.780625 0.49969482421875 0.205963134765625 -0.5877380371093779 +0.7807500000000001 0.49981689453125 0.206024169921875 -0.5877380371093779 +0.780875 0.49981689453125 0.206024169921875 -0.5877380371093779 +0.781 0.499908447265625 0.2060546875 -0.5877380371093779 +0.7811250000000001 0.49993896484375 0.2060546875 -0.5877380371093779 +0.78125 0.49993896484375 0.2060546875 -0.5877380371093779 +0.781375 0.499908447265625 0.2060546875 -0.5877380371093779 +0.7815 0.499908447265625 0.2060546875 -0.5877380371093779 +0.781625 0.49981689453125 0.206024169921875 -0.5877380371093779 +0.7817500000000001 0.49969482421875 0.205963134765625 -0.5877380371093779 +0.781875 0.49969482421875 0.205963134765625 -0.5877380371093779 +0.782 0.49951171875 0.20587158203125 -0.5877380371093779 +0.7821250000000001 0.49951171875 0.20587158203125 -0.5877380371093779 +0.78225 0.499267578125 0.205780029296875 -0.5877380371093779 +0.782375 0.49896240234375 0.205657958984375 -0.5877380371093779 +0.7825 0.49896240234375 0.205657958984375 -0.5877380371093779 +0.782625 0.49859619140625 0.20550537109375 -0.5877380371093779 +0.7827500000000001 0.49859619140625 0.20550537109375 -0.5877380371093779 +0.782875 0.498199462890625 0.205352783203125 -0.5877380371093779 +0.783 0.497711181640625 0.20513916015625 -0.5877380371093779 +0.7831250000000001 0.497711181640625 0.20513916015625 -0.5877380371093779 +0.78325 0.4971923828125 0.204925537109375 -0.5877380371093779 +0.783375 0.4971923828125 0.204925537109375 -0.5877380371093779 +0.7835 0.496612548828125 0.204681396484375 -0.5877380371093779 +0.783625 0.496002197265625 0.204437255859375 -0.5877380371093779 +0.7837500000000001 0.496002197265625 0.204437255859375 -0.5877380371093779 +0.783875 0.49530029296875 0.20416259765625 -0.5877380371093779 +0.784 0.49530029296875 0.20416259765625 -0.5877380371093779 +0.784125 0.49456787109375 0.203857421875 -0.5877380371093779 +0.78425 0.4937744140625 0.203521728515625 -0.5877380371093779 +0.784375 0.4937744140625 0.203521728515625 -0.5877380371093779 +0.7845 0.492950439453125 0.20318603515625 -0.5877380371093779 +0.784625 0.492950439453125 0.20318603515625 -0.5877380371093779 +0.7847500000000001 0.492034912109375 0.20281982421875 -0.5877380371093779 +0.784875 0.4910888671875 0.202423095703125 -0.5877380371093779 +0.785 0.4910888671875 0.202423095703125 -0.5877380371093779 +0.785125 0.490081787109375 0.201995849609375 -0.5877380371093779 +0.78525 0.490081787109375 0.201995849609375 -0.5877380371093779 +0.785375 0.489013671875 0.201568603515625 -0.5877380371093779 +0.7855 0.487884521484375 0.201080322265625 -0.5877380371093779 +0.785625 0.487884521484375 0.201080322265625 -0.5877380371093779 +0.7857500000000001 0.486724853515625 0.20062255859375 -0.5877380371093779 +0.785875 0.486724853515625 0.20062255859375 -0.5877380371093779 +0.786 0.485504150390625 0.200103759765625 -0.5877380371093779 +0.786125 0.484222412109375 0.1995849609375 -0.5877380371093779 +0.78625 0.484222412109375 0.1995849609375 -0.5877380371093779 +0.786375 0.48291015625 0.19903564453125 -0.5877380371093779 +0.7865 0.48291015625 0.19903564453125 -0.5877380371093779 +0.786625 0.48150634765625 0.198455810546875 -0.5877380371093779 +0.7867500000000001 0.480072021484375 0.1978759765625 -0.5877380371093779 +0.786875 0.480072021484375 0.1978759765625 -0.5877380371093779 +0.787 0.478607177734375 0.197265625 -0.5877380371093779 +0.787125 0.478607177734375 0.197265625 -0.5877380371093779 +0.78725 0.47705078125 0.196624755859375 -0.5877380371093779 +0.787375 0.4754638671875 0.19598388671875 -0.5877380371093779 +0.7875 0.4754638671875 0.19598388671875 -0.5877380371093779 +0.787625 0.47381591796875 0.195281982421875 -0.5877380371093779 +0.7877500000000001 0.47381591796875 0.195281982421875 -0.5877380371093779 +0.787875 0.472137451171875 0.194610595703125 -0.5877380371093779 +0.788 0.470367431640625 0.193878173828125 -0.5877380371093779 +0.788125 0.470367431640625 0.193878173828125 -0.5877380371093779 +0.78825 0.46856689453125 0.193145751953125 -0.5877380371093779 +0.788375 0.46856689453125 0.193145751953125 -0.5877380371093779 +0.7885 0.46673583984375 0.1923828125 -0.5877380371093779 +0.788625 0.464813232421875 0.19158935546875 -0.5877380371093779 +0.7887500000000001 0.464813232421875 0.19158935546875 -0.5877380371093779 +0.788875 0.462890625 0.1907958984375 -0.5877380371093779 +0.789 0.462890625 0.1907958984375 -0.5877380371093779 +0.789125 0.46087646484375 0.189971923828125 -0.5877380371093779 +0.78925 0.458831787109375 0.189117431640625 -0.5877380371093779 +0.789375 0.458831787109375 0.189117431640625 -0.5877380371093779 +0.7895 0.45672607421875 0.188232421875 -0.5877380371093779 +0.789625 0.45672607421875 0.188232421875 -0.5877380371093779 +0.7897500000000001 0.454559326171875 0.187347412109375 -0.5877380371093779 +0.789875 0.452362060546875 0.18646240234375 -0.5877380371093779 +0.79 0.452362060546875 0.18646240234375 -0.5877380371093779 +0.790125 0.450103759765625 0.185516357421875 -0.5877380371093779 +0.79025 0.450103759765625 0.185516357421875 -0.5877380371093779 +0.790375 0.447784423828125 0.1845703125 -0.5877380371093779 +0.7905 0.4454345703125 0.18359375 -0.5877380371093779 +0.790625 0.4454345703125 0.18359375 -0.5877380371093779 +0.7907500000000001 0.44305419921875 0.1826171875 -0.5877380371093779 +0.790875 0.44305419921875 0.1826171875 -0.5877380371093779 +0.791 0.440582275390625 0.181610107421875 -0.5877380371093779 +0.791125 0.438079833984375 0.180572509765625 -0.5877380371093779 +0.79125 0.438079833984375 0.180572509765625 -0.5877380371093779 +0.7913750000000001 0.435546875 0.179534912109375 -0.5877380371093779 +0.7915 0.435546875 0.179534912109375 -0.5877380371093779 +0.791625 0.432952880859375 0.178466796875 -0.5877380371093779 +0.7917500000000001 0.4302978515625 0.1773681640625 -0.5877380371093779 +0.791875 0.4302978515625 0.1773681640625 -0.5877380371093779 +0.792 0.4276123046875 0.176239013671875 -0.5877380371093779 +0.792125 0.4276123046875 0.176239013671875 -0.5877380371093779 +0.79225 0.424896240234375 0.175140380859375 -0.5877380371093779 +0.7923750000000001 0.422119140625 0.173980712890625 -0.5877380371093779 +0.7925 0.422119140625 0.173980712890625 -0.5877380371093779 +0.792625 0.419281005859375 0.172821044921875 -0.5877380371093779 +0.7927500000000001 0.419281005859375 0.172821044921875 -0.5877380371093779 +0.792875 0.416412353515625 0.171630859375 -0.5877380371093779 +0.793 0.413482666015625 0.17041015625 -0.5877380371093779 +0.793125 0.413482666015625 0.17041015625 -0.5877380371093779 +0.79325 0.4105224609375 0.169189453125 -0.5877380371093779 +0.7933750000000001 0.4105224609375 0.169189453125 -0.5877380371093779 +0.7935 0.407501220703125 0.16796875 -0.5877380371093779 +0.793625 0.404449462890625 0.166717529296875 -0.5877380371093779 +0.7937500000000001 0.404449462890625 0.166717529296875 -0.5877380371093779 +0.793875 0.401336669921875 0.165435791015625 -0.5877380371093779 +0.794 0.401336669921875 0.165435791015625 -0.5877380371093779 +0.794125 0.398193359375 0.16412353515625 -0.5877380371093779 +0.79425 0.39501953125 0.162811279296875 -0.5877380371093779 +0.7943750000000001 0.39501953125 0.162811279296875 -0.5877380371093779 +0.7945 0.39178466796875 0.1614990234375 -0.5877380371093779 +0.794625 0.39178466796875 0.1614990234375 -0.5877380371093779 +0.7947500000000001 0.388519287109375 0.160125732421875 -0.5877380371093779 +0.794875 0.38519287109375 0.158782958984375 -0.5877380371093779 +0.795 0.38519287109375 0.158782958984375 -0.5877380371093779 +0.795125 0.3818359375 0.157379150390625 -0.5877380371093779 +0.79525 0.3818359375 0.157379150390625 -0.5877380371093779 +0.7953750000000001 0.378448486328125 0.155975341796875 -0.5877380371093779 +0.7955 0.375 0.154571533203125 -0.5877380371093779 +0.795625 0.375 0.154571533203125 -0.5877380371093779 +0.7957500000000001 0.37152099609375 0.15313720703125 -0.5877380371093779 +0.795875 0.37152099609375 0.15313720703125 -0.5877380371093779 +0.796 0.36798095703125 0.15167236328125 -0.5877380371093779 +0.796125 0.36444091796875 0.15020751953125 -0.5877380371093779 +0.79625 0.36444091796875 0.15020751953125 -0.5877380371093779 +0.7963750000000001 0.360809326171875 0.148712158203125 -0.5877380371093779 +0.7965 0.360809326171875 0.148712158203125 -0.5877380371093779 +0.796625 0.357177734375 0.147216796875 -0.5877380371093779 +0.7967500000000001 0.353485107421875 0.14569091796875 -0.5877380371093779 +0.796875 0.353485107421875 0.14569091796875 -0.5877380371093779 +0.797 0.349761962890625 0.1441650390625 -0.5877380371093779 +0.797125 0.349761962890625 0.1441650390625 -0.5877380371093779 +0.79725 0.34600830078125 0.142608642578125 -0.5877380371093779 +0.7973750000000001 0.34222412109375 0.14105224609375 -0.5877380371093779 +0.7975 0.34222412109375 0.14105224609375 -0.5877380371093779 +0.797625 0.33837890625 0.13946533203125 -0.5877380371093779 +0.7977500000000001 0.33837890625 0.13946533203125 -0.5877380371093779 +0.797875 0.334503173828125 0.13787841796875 -0.5877380371093779 +0.798 0.330596923828125 0.136260986328125 -0.5877380371093779 +0.798125 0.330596923828125 0.136260986328125 -0.5877380371093779 +0.79825 0.32666015625 0.1346435546875 -0.5877380371093779 +0.7983750000000001 0.32666015625 0.1346435546875 -0.5877380371093779 +0.7985 0.322662353515625 0.13299560546875 -0.5877380371093779 +0.798625 0.31866455078125 0.13134765625 -0.5877380371093779 +0.7987500000000001 0.31866455078125 0.13134765625 -0.5877380371093779 +0.798875 0.314605712890625 0.129669189453125 -0.5877380371093779 +0.799 0.314605712890625 0.129669189453125 -0.5877380371093779 +0.799125 0.310516357421875 0.12799072265625 -0.5877380371093779 +0.79925 0.306396484375 0.12628173828125 -0.5877380371093779 +0.7993750000000001 0.306396484375 0.12628173828125 -0.5877380371093779 +0.7995 0.30224609375 0.12457275390625 -0.5877380371093779 +0.799625 0.30224609375 0.12457275390625 -0.5877380371093779 +0.7997500000000001 0.298065185546875 0.12286376953125 -0.5877380371093779 +0.799875 0.2938232421875 0.12109375 -0.5877380371093779 +0.8 0.2938232421875 0.041839599609375 -0.857464599609377 +0.800125 0.289581298828125 0.041229248046875 -0.857464599609377 +0.80025 0.289581298828125 0.041229248046875 -0.857464599609377 +0.8003750000000001 0.285308837890625 0.040618896484375 -0.857464599609377 +0.8005 0.280975341796875 0.040008544921875 -0.857464599609377 +0.800625 0.280975341796875 0.040008544921875 -0.857464599609377 +0.80075 0.276641845703125 0.039398193359375 -0.857464599609377 +0.800875 0.276641845703125 0.039398193359375 -0.857464599609377 +0.801 0.27227783203125 0.038787841796875 -0.857464599609377 +0.801125 0.267852783203125 0.03814697265625 -0.857464599609377 +0.80125 0.267852783203125 0.03814697265625 -0.857464599609377 +0.8013750000000001 0.263427734375 0.037506103515625 -0.857464599609377 +0.8015 0.263427734375 0.037506103515625 -0.857464599609377 +0.801625 0.25897216796875 0.036895751953125 -0.857464599609377 +0.80175 0.25445556640625 0.0362548828125 -0.857464599609377 +0.801875 0.25445556640625 0.0362548828125 -0.857464599609377 +0.802 0.249969482421875 0.03558349609375 -0.857464599609377 +0.802125 0.249969482421875 0.03558349609375 -0.857464599609377 +0.80225 0.24542236328125 0.034942626953125 -0.857464599609377 +0.8023750000000001 0.2408447265625 0.0343017578125 -0.857464599609377 +0.8025 0.2408447265625 0.0343017578125 -0.857464599609377 +0.802625 0.236236572265625 0.03363037109375 -0.857464599609377 +0.80275 0.236236572265625 0.03363037109375 -0.857464599609377 +0.802875 0.231597900390625 0.032989501953125 -0.857464599609377 +0.803 0.226959228515625 0.032318115234375 -0.857464599609377 +0.803125 0.226959228515625 0.032318115234375 -0.857464599609377 +0.80325 0.2222900390625 0.031646728515625 -0.857464599609377 +0.8033750000000001 0.2222900390625 0.031646728515625 -0.857464599609377 +0.8035 0.21759033203125 0.030975341796875 -0.857464599609377 +0.803625 0.212860107421875 0.030303955078125 -0.857464599609377 +0.80375 0.212860107421875 0.030303955078125 -0.857464599609377 +0.803875 0.208099365234375 0.029632568359375 -0.857464599609377 +0.804 0.208099365234375 0.029632568359375 -0.857464599609377 +0.804125 0.203338623046875 0.028961181640625 -0.857464599609377 +0.80425 0.19854736328125 0.02825927734375 -0.857464599609377 +0.8043750000000001 0.19854736328125 0.02825927734375 -0.857464599609377 +0.8045 0.1937255859375 0.027587890625 -0.857464599609377 +0.804625 0.1937255859375 0.027587890625 -0.857464599609377 +0.80475 0.188873291015625 0.026885986328125 -0.857464599609377 +0.804875 0.18402099609375 0.026214599609375 -0.857464599609377 +0.805 0.18402099609375 0.026214599609375 -0.857464599609377 +0.805125 0.17913818359375 0.0255126953125 -0.857464599609377 +0.80525 0.17913818359375 0.0255126953125 -0.857464599609377 +0.8053750000000001 0.17425537109375 0.024810791015625 -0.857464599609377 +0.8055 0.169342041015625 0.02410888671875 -0.857464599609377 +0.805625 0.169342041015625 0.02410888671875 -0.857464599609377 +0.80575 0.164398193359375 0.023406982421875 -0.857464599609377 +0.805875 0.164398193359375 0.023406982421875 -0.857464599609377 +0.806 0.159454345703125 0.022705078125 -0.857464599609377 +0.806125 0.15447998046875 0.022003173828125 -0.857464599609377 +0.80625 0.15447998046875 0.022003173828125 -0.857464599609377 +0.8063750000000001 0.14947509765625 0.021270751953125 -0.857464599609377 +0.8065 0.14947509765625 0.021270751953125 -0.857464599609377 +0.806625 0.14447021484375 0.02056884765625 -0.857464599609377 +0.80675 0.13946533203125 0.019866943359375 -0.857464599609377 +0.806875 0.13946533203125 0.019866943359375 -0.857464599609377 +0.8070000000000001 0.134429931640625 0.019134521484375 -0.857464599609377 +0.807125 0.134429931640625 0.019134521484375 -0.857464599609377 +0.80725 0.129364013671875 0.0184326171875 -0.857464599609377 +0.8073750000000001 0.124298095703125 0.0177001953125 -0.857464599609377 +0.8075 0.124298095703125 0.0177001953125 -0.857464599609377 +0.807625 0.119232177734375 0.0169677734375 -0.857464599609377 +0.80775 0.119232177734375 0.0169677734375 -0.857464599609377 +0.807875 0.1141357421875 0.0162353515625 -0.857464599609377 +0.8080000000000001 0.109039306640625 0.015533447265625 -0.857464599609377 +0.808125 0.109039306640625 0.015533447265625 -0.857464599609377 +0.80825 0.103912353515625 0.014801025390625 -0.857464599609377 +0.8083750000000001 0.103912353515625 0.014801025390625 -0.857464599609377 +0.8085 0.098785400390625 0.014068603515625 -0.857464599609377 +0.808625 0.093658447265625 0.013336181640625 -0.857464599609377 +0.80875 0.093658447265625 0.013336181640625 -0.857464599609377 +0.808875 0.0885009765625 0.012603759765625 -0.857464599609377 +0.8090000000000001 0.0885009765625 0.012603759765625 -0.857464599609377 +0.809125 0.083343505859375 0.011871337890625 -0.857464599609377 +0.80925 0.07818603515625 0.011138916015625 -0.857464599609377 +0.8093750000000001 0.07818603515625 0.011138916015625 -0.857464599609377 +0.8095 0.072998046875 0.0103759765625 -0.857464599609377 +0.809625 0.072998046875 0.0103759765625 -0.857464599609377 +0.80975 0.06781005859375 0.0096435546875 -0.857464599609377 +0.809875 0.0626220703125 0.0089111328125 -0.857464599609377 +0.8100000000000001 0.0626220703125 0.0089111328125 -0.857464599609377 +0.810125 0.05743408203125 0.0081787109375 -0.857464599609377 +0.81025 0.05743408203125 0.0081787109375 -0.857464599609377 +0.8103750000000001 0.05224609375 0.007415771484375 -0.857464599609377 +0.8105 0.047027587890625 0.006683349609375 -0.857464599609377 +0.810625 0.047027587890625 0.006683349609375 -0.857464599609377 +0.81075 0.04180908203125 0.005950927734375 -0.857464599609377 +0.810875 0.04180908203125 0.005950927734375 -0.857464599609377 +0.8110000000000001 0.036590576171875 0.00518798828125 -0.857464599609377 +0.811125 0.0313720703125 0.00445556640625 -0.857464599609377 +0.81125 0.0313720703125 0.00445556640625 -0.857464599609377 +0.8113750000000001 0.026123046875 0.00372314453125 -0.857464599609377 +0.8115 0.026123046875 0.00372314453125 -0.857464599609377 +0.811625 0.020904541015625 0.002960205078125 -0.857464599609377 +0.81175 0.01568603515625 0.002227783203125 -0.857464599609377 +0.811875 0.01568603515625 0.002227783203125 -0.857464599609377 +0.8120000000000001 0.01043701171875 0.00146484375 -0.857464599609377 +0.812125 0.01043701171875 0.00146484375 -0.857464599609377 +0.81225 0.005218505859375 0.000732421875 -0.857464599609377 +0.8123750000000001 0.0 0.0 -0.857464599609377 +0.8125 0.0 0.0 -0.857464599609377 +0.812625 -0.005218505859375 -0.000732421875 -0.857464599609377 +0.81275 -0.005218505859375 -0.000732421875 -0.857464599609377 +0.812875 -0.01043701171875 -0.00146484375 -0.857464599609377 +0.8130000000000001 -0.01568603515625 -0.002227783203125 -0.857464599609377 +0.813125 -0.01568603515625 -0.002227783203125 -0.857464599609377 +0.81325 -0.020904541015625 -0.002960205078125 -0.857464599609377 +0.8133750000000001 -0.020904541015625 -0.002960205078125 -0.857464599609377 +0.8135 -0.026123046875 -0.00372314453125 -0.857464599609377 +0.813625 -0.0313720703125 -0.00445556640625 -0.857464599609377 +0.81375 -0.0313720703125 -0.00445556640625 -0.857464599609377 +0.813875 -0.036590576171875 -0.00518798828125 -0.857464599609377 +0.8140000000000001 -0.036590576171875 -0.00518798828125 -0.857464599609377 +0.814125 -0.04180908203125 -0.005950927734375 -0.857464599609377 +0.81425 -0.047027587890625 -0.006683349609375 -0.857464599609377 +0.8143750000000001 -0.047027587890625 -0.006683349609375 -0.857464599609377 +0.8145 -0.05224609375 -0.007415771484375 -0.857464599609377 +0.814625 -0.05224609375 -0.007415771484375 -0.857464599609377 +0.81475 -0.05743408203125 -0.0081787109375 -0.857464599609377 +0.814875 -0.0626220703125 -0.0089111328125 -0.857464599609377 +0.8150000000000001 -0.0626220703125 -0.0089111328125 -0.857464599609377 +0.815125 -0.06781005859375 -0.0096435546875 -0.857464599609377 +0.81525 -0.06781005859375 -0.0096435546875 -0.857464599609377 +0.8153750000000001 -0.072998046875 -0.0103759765625 -0.857464599609377 +0.8155 -0.07818603515625 -0.011138916015625 -0.857464599609377 +0.815625 -0.07818603515625 -0.011138916015625 -0.857464599609377 +0.81575 -0.083343505859375 -0.011871337890625 -0.857464599609377 +0.815875 -0.083343505859375 -0.011871337890625 -0.857464599609377 +0.8160000000000001 -0.0885009765625 -0.012603759765625 -0.857464599609377 +0.816125 -0.093658447265625 -0.013336181640625 -0.857464599609377 +0.81625 -0.093658447265625 -0.013336181640625 -0.857464599609377 +0.816375 -0.098785400390625 -0.014068603515625 -0.857464599609377 +0.8165 -0.098785400390625 -0.014068603515625 -0.857464599609377 +0.816625 -0.103912353515625 -0.014801025390625 -0.857464599609377 +0.81675 -0.109039306640625 -0.015533447265625 -0.857464599609377 +0.816875 -0.109039306640625 -0.015533447265625 -0.857464599609377 +0.8170000000000001 -0.1141357421875 -0.0162353515625 -0.857464599609377 +0.817125 -0.1141357421875 -0.0162353515625 -0.857464599609377 +0.81725 -0.119232177734375 -0.0169677734375 -0.857464599609377 +0.817375 -0.124298095703125 -0.0177001953125 -0.857464599609377 +0.8175 -0.124298095703125 -0.0177001953125 -0.857464599609377 +0.817625 -0.129364013671875 -0.0184326171875 -0.857464599609377 +0.81775 -0.129364013671875 -0.0184326171875 -0.857464599609377 +0.817875 -0.134429931640625 -0.019134521484375 -0.857464599609377 +0.8180000000000001 -0.13946533203125 -0.019866943359375 -0.857464599609377 +0.818125 -0.13946533203125 -0.019866943359375 -0.857464599609377 +0.81825 -0.14447021484375 -0.02056884765625 -0.857464599609377 +0.818375 -0.14447021484375 -0.02056884765625 -0.857464599609377 +0.8185 -0.14947509765625 -0.021270751953125 -0.857464599609377 +0.818625 -0.15447998046875 -0.022003173828125 -0.857464599609377 +0.81875 -0.15447998046875 -0.022003173828125 -0.857464599609377 +0.818875 -0.159454345703125 -0.022705078125 -0.857464599609377 +0.8190000000000001 -0.159454345703125 -0.022705078125 -0.857464599609377 +0.819125 -0.164398193359375 -0.023406982421875 -0.857464599609377 +0.81925 -0.169342041015625 -0.02410888671875 -0.857464599609377 +0.819375 -0.169342041015625 -0.02410888671875 -0.857464599609377 +0.8195 -0.17425537109375 -0.024810791015625 -0.857464599609377 +0.819625 -0.17425537109375 -0.024810791015625 -0.857464599609377 +0.81975 -0.17913818359375 -0.0255126953125 -0.857464599609377 +0.819875 -0.18402099609375 -0.026214599609375 -0.857464599609377 +0.8200000000000001 -0.18402099609375 -0.026214599609375 -0.857464599609377 +0.820125 -0.188873291015625 -0.026885986328125 -0.857464599609377 +0.82025 -0.188873291015625 -0.026885986328125 -0.857464599609377 +0.820375 -0.1937255859375 -0.027587890625 -0.857464599609377 +0.8205 -0.19854736328125 -0.02825927734375 -0.857464599609377 +0.820625 -0.19854736328125 -0.02825927734375 -0.857464599609377 +0.82075 -0.203338623046875 -0.028961181640625 -0.857464599609377 +0.820875 -0.203338623046875 -0.028961181640625 -0.857464599609377 +0.8210000000000001 -0.208099365234375 -0.029632568359375 -0.857464599609377 +0.821125 -0.212860107421875 -0.030303955078125 -0.857464599609377 +0.82125 -0.212860107421875 -0.030303955078125 -0.857464599609377 +0.821375 -0.21759033203125 -0.030975341796875 -0.857464599609377 +0.8215 -0.21759033203125 -0.030975341796875 -0.857464599609377 +0.821625 -0.2222900390625 -0.031646728515625 -0.857464599609377 +0.82175 -0.226959228515625 -0.032318115234375 -0.857464599609377 +0.821875 -0.226959228515625 -0.032318115234375 -0.857464599609377 +0.8220000000000001 -0.231597900390625 -0.032989501953125 -0.857464599609377 +0.822125 -0.231597900390625 -0.032989501953125 -0.857464599609377 +0.82225 -0.236236572265625 -0.03363037109375 -0.857464599609377 +0.822375 -0.2408447265625 -0.0343017578125 -0.857464599609377 +0.8225 -0.2408447265625 -0.0343017578125 -0.857464599609377 +0.8226250000000001 -0.24542236328125 -0.034942626953125 -0.857464599609377 +0.82275 -0.24542236328125 -0.034942626953125 -0.857464599609377 +0.822875 -0.249969482421875 -0.03558349609375 -0.857464599609377 +0.8230000000000001 -0.25445556640625 -0.0362548828125 -0.857464599609377 +0.823125 -0.25445556640625 -0.0362548828125 -0.857464599609377 +0.82325 -0.25897216796875 -0.036895751953125 -0.857464599609377 +0.823375 -0.25897216796875 -0.036895751953125 -0.857464599609377 +0.8235 -0.263427734375 -0.037506103515625 -0.857464599609377 +0.8236250000000001 -0.267852783203125 -0.03814697265625 -0.857464599609377 +0.82375 -0.267852783203125 -0.03814697265625 -0.857464599609377 +0.823875 -0.27227783203125 -0.038787841796875 -0.857464599609377 +0.8240000000000001 -0.27227783203125 -0.038787841796875 -0.857464599609377 +0.824125 -0.276641845703125 -0.039398193359375 -0.857464599609377 +0.82425 -0.280975341796875 -0.040008544921875 -0.857464599609377 +0.824375 -0.280975341796875 -0.040008544921875 -0.857464599609377 +0.8245 -0.285308837890625 -0.040618896484375 -0.857464599609377 +0.8246250000000001 -0.285308837890625 -0.040618896484375 -0.857464599609377 +0.82475 -0.289581298828125 -0.041229248046875 -0.857464599609377 +0.824875 -0.2938232421875 -0.041839599609375 -0.857464599609377 +0.8250000000000001 -0.2938232421875 -0.041839599609375 -0.857464599609377 +0.825125 -0.298065185546875 -0.042449951171875 -0.857464599609377 +0.82525 -0.298065185546875 -0.042449951171875 -0.857464599609377 +0.825375 -0.30224609375 -0.043060302734375 -0.857464599609377 +0.8255 -0.306396484375 -0.04364013671875 -0.857464599609377 +0.8256250000000001 -0.306396484375 -0.04364013671875 -0.857464599609377 +0.82575 -0.310516357421875 -0.044219970703125 -0.857464599609377 +0.825875 -0.310516357421875 -0.044219970703125 -0.857464599609377 +0.8260000000000001 -0.314605712890625 -0.0447998046875 -0.857464599609377 +0.826125 -0.31866455078125 -0.045379638671875 -0.857464599609377 +0.82625 -0.31866455078125 -0.045379638671875 -0.857464599609377 +0.826375 -0.322662353515625 -0.04595947265625 -0.857464599609377 +0.8265 -0.322662353515625 -0.04595947265625 -0.857464599609377 +0.8266250000000001 -0.32666015625 -0.0465087890625 -0.857464599609377 +0.82675 -0.330596923828125 -0.047088623046875 -0.857464599609377 +0.826875 -0.330596923828125 -0.047088623046875 -0.857464599609377 +0.8270000000000001 -0.334503173828125 -0.047637939453125 -0.857464599609377 +0.827125 -0.334503173828125 -0.047637939453125 -0.857464599609377 +0.82725 -0.33837890625 -0.048187255859375 -0.857464599609377 +0.827375 -0.34222412109375 -0.048736572265625 -0.857464599609377 +0.8275 -0.34222412109375 -0.048736572265625 -0.857464599609377 +0.8276250000000001 -0.34600830078125 -0.049285888671875 -0.857464599609377 +0.82775 -0.34600830078125 -0.049285888671875 -0.857464599609377 +0.827875 -0.349761962890625 -0.0498046875 -0.857464599609377 +0.8280000000000001 -0.353485107421875 -0.05035400390625 -0.857464599609377 +0.828125 -0.353485107421875 -0.05035400390625 -0.857464599609377 +0.82825 -0.357177734375 -0.050872802734375 -0.857464599609377 +0.828375 -0.357177734375 -0.050872802734375 -0.857464599609377 +0.8285 -0.360809326171875 -0.0513916015625 -0.857464599609377 +0.8286250000000001 -0.36444091796875 -0.051910400390625 -0.857464599609377 +0.82875 -0.36444091796875 -0.051910400390625 -0.857464599609377 +0.828875 -0.36798095703125 -0.052398681640625 -0.857464599609377 +0.8290000000000001 -0.36798095703125 -0.052398681640625 -0.857464599609377 +0.829125 -0.37152099609375 -0.05291748046875 -0.857464599609377 +0.82925 -0.375 -0.05340576171875 -0.857464599609377 +0.829375 -0.375 -0.05340576171875 -0.857464599609377 +0.8295 -0.378448486328125 -0.05389404296875 -0.857464599609377 +0.8296250000000001 -0.378448486328125 -0.05389404296875 -0.857464599609377 +0.82975 -0.3818359375 -0.05438232421875 -0.857464599609377 +0.829875 -0.38519287109375 -0.05487060546875 -0.857464599609377 +0.8300000000000001 -0.38519287109375 -0.05487060546875 -0.857464599609377 +0.830125 -0.388519287109375 -0.055328369140625 -0.857464599609377 +0.83025 -0.388519287109375 -0.055328369140625 -0.857464599609377 +0.830375 -0.39178466796875 -0.055816650390625 -0.857464599609377 +0.8305 -0.39501953125 -0.0562744140625 -0.857464599609377 +0.8306250000000001 -0.39501953125 -0.0562744140625 -0.857464599609377 +0.83075 -0.398193359375 -0.05670166015625 -0.857464599609377 +0.830875 -0.398193359375 -0.05670166015625 -0.857464599609377 +0.8310000000000001 -0.401336669921875 -0.057159423828125 -0.857464599609377 +0.831125 -0.404449462890625 -0.0576171875 -0.857464599609377 +0.83125 -0.404449462890625 -0.0576171875 -0.857464599609377 +0.831375 -0.407501220703125 -0.05804443359375 -0.857464599609377 +0.8315 -0.407501220703125 -0.05804443359375 -0.857464599609377 +0.8316250000000001 -0.4105224609375 -0.0584716796875 -0.857464599609377 +0.83175 -0.413482666015625 -0.05889892578125 -0.857464599609377 +0.831875 -0.413482666015625 -0.05889892578125 -0.857464599609377 +0.8320000000000001 -0.416412353515625 -0.003936767578125 -0.9903991699218756 +0.832125 -0.416412353515625 -0.003936767578125 -0.9903991699218756 +0.83225 -0.419281005859375 -0.00396728515625 -0.9903991699218756 +0.832375 -0.422119140625 -0.003997802734375 -0.9903991699218756 +0.8325 -0.422119140625 -0.003997802734375 -0.9903991699218756 +0.8326250000000001 -0.424896240234375 -0.0040283203125 -0.9903991699218756 +0.83275 -0.424896240234375 -0.0040283203125 -0.9903991699218756 +0.832875 -0.4276123046875 -0.004058837890625 -0.9903991699218756 +0.833 -0.4302978515625 -0.00408935546875 -0.9903991699218756 +0.833125 -0.4302978515625 -0.00408935546875 -0.9903991699218756 +0.83325 -0.432952880859375 -0.004119873046875 -0.9903991699218756 +0.833375 -0.432952880859375 -0.004119873046875 -0.9903991699218756 +0.8335 -0.435546875 -0.004119873046875 -0.9903991699218756 +0.8336250000000001 -0.438079833984375 -0.004150390625 -0.9903991699218756 +0.83375 -0.438079833984375 -0.004150390625 -0.9903991699218756 +0.833875 -0.440582275390625 -0.004180908203125 -0.9903991699218756 +0.834 -0.440582275390625 -0.004180908203125 -0.9903991699218756 +0.834125 -0.44305419921875 -0.00421142578125 -0.9903991699218756 +0.83425 -0.4454345703125 -0.00421142578125 -0.9903991699218756 +0.834375 -0.4454345703125 -0.00421142578125 -0.9903991699218756 +0.8345 -0.447784423828125 -0.004241943359375 -0.9903991699218756 +0.8346250000000001 -0.447784423828125 -0.004241943359375 -0.9903991699218756 +0.83475 -0.450103759765625 -0.0042724609375 -0.9903991699218756 +0.834875 -0.452362060546875 -0.004302978515625 -0.9903991699218756 +0.835 -0.452362060546875 -0.004302978515625 -0.9903991699218756 +0.835125 -0.454559326171875 -0.004302978515625 -0.9903991699218756 +0.83525 -0.454559326171875 -0.004302978515625 -0.9903991699218756 +0.835375 -0.45672607421875 -0.00433349609375 -0.9903991699218756 +0.8355 -0.458831787109375 -0.004364013671875 -0.9903991699218756 +0.8356250000000001 -0.458831787109375 -0.004364013671875 -0.9903991699218756 +0.83575 -0.46087646484375 -0.004364013671875 -0.9903991699218756 +0.835875 -0.46087646484375 -0.004364013671875 -0.9903991699218756 +0.836 -0.462890625 -0.00439453125 -0.9903991699218756 +0.836125 -0.464813232421875 -0.004425048828125 -0.9903991699218756 +0.83625 -0.464813232421875 -0.004425048828125 -0.9903991699218756 +0.836375 -0.46673583984375 -0.004425048828125 -0.9903991699218756 +0.8365 -0.46673583984375 -0.004425048828125 -0.9903991699218756 +0.8366250000000001 -0.46856689453125 -0.00445556640625 -0.9903991699218756 +0.83675 -0.470367431640625 -0.00445556640625 -0.9903991699218756 +0.836875 -0.470367431640625 -0.00445556640625 -0.9903991699218756 +0.837 -0.472137451171875 -0.004486083984375 -0.9903991699218756 +0.837125 -0.472137451171875 -0.004486083984375 -0.9903991699218756 +0.83725 -0.47381591796875 -0.004486083984375 -0.9903991699218756 +0.837375 -0.4754638671875 -0.0045166015625 -0.9903991699218756 +0.8375 -0.4754638671875 -0.0045166015625 -0.9903991699218756 +0.8376250000000001 -0.47705078125 -0.0045166015625 -0.9903991699218756 +0.83775 -0.47705078125 -0.0045166015625 -0.9903991699218756 +0.837875 -0.478607177734375 -0.004547119140625 -0.9903991699218756 +0.838 -0.480072021484375 -0.004547119140625 -0.9903991699218756 +0.838125 -0.480072021484375 -0.004547119140625 -0.9903991699218756 +0.8382500000000001 -0.48150634765625 -0.00457763671875 -0.9903991699218756 +0.838375 -0.48150634765625 -0.00457763671875 -0.9903991699218756 +0.8385 -0.48291015625 -0.00457763671875 -0.9903991699218756 +0.8386250000000001 -0.484222412109375 -0.004608154296875 -0.9903991699218756 +0.83875 -0.484222412109375 -0.004608154296875 -0.9903991699218756 +0.838875 -0.485504150390625 -0.004608154296875 -0.9903991699218756 +0.839 -0.485504150390625 -0.004608154296875 -0.9903991699218756 +0.839125 -0.486724853515625 -0.004608154296875 -0.9903991699218756 +0.8392500000000001 -0.487884521484375 -0.004638671875 -0.9903991699218756 +0.839375 -0.487884521484375 -0.004638671875 -0.9903991699218756 +0.8395 -0.489013671875 -0.004638671875 -0.9903991699218756 +0.8396250000000001 -0.489013671875 -0.004638671875 -0.9903991699218756 +0.83975 -0.490081787109375 -0.004638671875 -0.9903991699218756 +0.839875 -0.4910888671875 -0.004669189453125 -0.9903991699218756 +0.84 -0.4910888671875 -0.004669189453125 -0.9903991699218756 +0.840125 -0.492034912109375 -0.004669189453125 -0.9903991699218756 +0.8402500000000001 -0.492034912109375 -0.004669189453125 -0.9903991699218756 +0.840375 -0.492950439453125 -0.004669189453125 -0.9903991699218756 +0.8405 -0.4937744140625 -0.00469970703125 -0.9903991699218756 +0.8406250000000001 -0.4937744140625 -0.00469970703125 -0.9903991699218756 +0.84075 -0.49456787109375 -0.00469970703125 -0.9903991699218756 +0.840875 -0.49456787109375 -0.00469970703125 -0.9903991699218756 +0.841 -0.49530029296875 -0.00469970703125 -0.9903991699218756 +0.841125 -0.496002197265625 -0.00469970703125 -0.9903991699218756 +0.8412500000000001 -0.496002197265625 -0.00469970703125 -0.9903991699218756 +0.841375 -0.496612548828125 -0.00469970703125 -0.9903991699218756 +0.8415 -0.496612548828125 -0.00469970703125 -0.9903991699218756 +0.8416250000000001 -0.4971923828125 -0.004730224609375 -0.9903991699218756 +0.84175 -0.497711181640625 -0.004730224609375 -0.9903991699218756 +0.841875 -0.497711181640625 -0.004730224609375 -0.9903991699218756 +0.842 -0.498199462890625 -0.004730224609375 -0.9903991699218756 +0.842125 -0.498199462890625 -0.004730224609375 -0.9903991699218756 +0.8422500000000001 -0.49859619140625 -0.004730224609375 -0.9903991699218756 +0.842375 -0.49896240234375 -0.004730224609375 -0.9903991699218756 +0.8425 -0.49896240234375 -0.004730224609375 -0.9903991699218756 +0.8426250000000001 -0.499267578125 -0.004730224609375 -0.9903991699218756 +0.84275 -0.499267578125 -0.004730224609375 -0.9903991699218756 +0.842875 -0.49951171875 -0.004730224609375 -0.9903991699218756 +0.843 -0.49969482421875 -0.004730224609375 -0.9903991699218756 +0.843125 -0.49969482421875 -0.004730224609375 -0.9903991699218756 +0.8432500000000001 -0.49981689453125 -0.004730224609375 -0.9903991699218756 +0.843375 -0.49981689453125 -0.004730224609375 -0.9903991699218756 +0.8435 -0.499908447265625 -0.004730224609375 -0.9903991699218756 +0.8436250000000001 -0.49993896484375 -0.004730224609375 -0.9903991699218756 +0.84375 -0.49993896484375 -0.004730224609375 -0.9903991699218756 +0.843875 -0.499908447265625 -0.004730224609375 -0.9903991699218756 +0.844 -0.499908447265625 -0.004730224609375 -0.9903991699218756 +0.844125 -0.49981689453125 -0.004730224609375 -0.9903991699218756 +0.8442500000000001 -0.49969482421875 -0.004730224609375 -0.9903991699218756 +0.844375 -0.49969482421875 -0.004730224609375 -0.9903991699218756 +0.8445 -0.49951171875 -0.004730224609375 -0.9903991699218756 +0.8446250000000001 -0.49951171875 -0.004730224609375 -0.9903991699218756 +0.84475 -0.499267578125 -0.004730224609375 -0.9903991699218756 +0.844875 -0.49896240234375 -0.004730224609375 -0.9903991699218756 +0.845 -0.49896240234375 -0.004730224609375 -0.9903991699218756 +0.845125 -0.49859619140625 -0.004730224609375 -0.9903991699218756 +0.8452500000000001 -0.49859619140625 -0.004730224609375 -0.9903991699218756 +0.845375 -0.498199462890625 -0.004730224609375 -0.9903991699218756 +0.8455 -0.497711181640625 -0.004730224609375 -0.9903991699218756 +0.8456250000000001 -0.497711181640625 -0.004730224609375 -0.9903991699218756 +0.84575 -0.4971923828125 -0.004730224609375 -0.9903991699218756 +0.845875 -0.4971923828125 -0.004730224609375 -0.9903991699218756 +0.846 -0.496612548828125 -0.00469970703125 -0.9903991699218756 +0.846125 -0.496002197265625 -0.00469970703125 -0.9903991699218756 +0.8462500000000001 -0.496002197265625 -0.00469970703125 -0.9903991699218756 +0.846375 -0.49530029296875 -0.00469970703125 -0.9903991699218756 +0.8465 -0.49530029296875 -0.00469970703125 -0.9903991699218756 +0.8466250000000001 -0.49456787109375 -0.00469970703125 -0.9903991699218756 +0.84675 -0.4937744140625 -0.00469970703125 -0.9903991699218756 +0.846875 -0.4937744140625 -0.00469970703125 -0.9903991699218756 +0.847 -0.492950439453125 -0.004669189453125 -0.9903991699218756 +0.847125 -0.492950439453125 -0.004669189453125 -0.9903991699218756 +0.8472500000000001 -0.492034912109375 -0.004669189453125 -0.9903991699218756 +0.847375 -0.4910888671875 -0.004669189453125 -0.9903991699218756 +0.8475 -0.4910888671875 -0.004669189453125 -0.9903991699218756 +0.8476250000000001 -0.490081787109375 -0.004638671875 -0.9903991699218756 +0.84775 -0.490081787109375 -0.004638671875 -0.9903991699218756 +0.847875 -0.489013671875 -0.004638671875 -0.9903991699218756 +0.848 -0.487884521484375 -0.004638671875 -0.9903991699218756 +0.848125 -0.487884521484375 -0.004638671875 -0.9903991699218756 +0.8482500000000001 -0.486724853515625 -0.004608154296875 -0.9903991699218756 +0.848375 -0.486724853515625 -0.004608154296875 -0.9903991699218756 +0.8485 -0.485504150390625 -0.004608154296875 -0.9903991699218756 +0.848625 -0.484222412109375 -0.004608154296875 -0.9903991699218756 +0.84875 -0.484222412109375 -0.004608154296875 -0.9903991699218756 +0.848875 -0.48291015625 -0.00457763671875 -0.9903991699218756 +0.849 -0.48291015625 -0.00457763671875 -0.9903991699218756 +0.849125 -0.48150634765625 -0.00457763671875 -0.9903991699218756 +0.8492500000000001 -0.480072021484375 -0.004547119140625 -0.9903991699218756 +0.849375 -0.480072021484375 -0.004547119140625 -0.9903991699218756 +0.8495 -0.478607177734375 -0.004547119140625 -0.9903991699218756 +0.849625 -0.478607177734375 -0.004547119140625 -0.9903991699218756 +0.84975 -0.47705078125 -0.0045166015625 -0.9903991699218756 +0.849875 -0.4754638671875 -0.0045166015625 -0.9903991699218756 +0.85 -0.4754638671875 -0.0045166015625 -0.9903991699218756 +0.850125 -0.47381591796875 -0.004486083984375 -0.9903991699218756 +0.8502500000000001 -0.47381591796875 -0.004486083984375 -0.9903991699218756 +0.850375 -0.472137451171875 -0.004486083984375 -0.9903991699218756 +0.8505 -0.470367431640625 -0.00445556640625 -0.9903991699218756 +0.850625 -0.470367431640625 -0.00445556640625 -0.9903991699218756 +0.85075 -0.46856689453125 -0.00445556640625 -0.9903991699218756 +0.850875 -0.46856689453125 -0.00445556640625 -0.9903991699218756 +0.851 -0.46673583984375 -0.004425048828125 -0.9903991699218756 +0.851125 -0.464813232421875 -0.004425048828125 -0.9903991699218756 +0.8512500000000001 -0.464813232421875 -0.004425048828125 -0.9903991699218756 +0.851375 -0.462890625 -0.00439453125 -0.9903991699218756 +0.8515 -0.462890625 -0.00439453125 -0.9903991699218756 +0.851625 -0.46087646484375 -0.004364013671875 -0.9903991699218756 +0.85175 -0.458831787109375 -0.004364013671875 -0.9903991699218756 +0.851875 -0.458831787109375 -0.004364013671875 -0.9903991699218756 +0.852 -0.45672607421875 -0.00433349609375 -0.9903991699218756 +0.852125 -0.45672607421875 -0.00433349609375 -0.9903991699218756 +0.8522500000000001 -0.454559326171875 -0.004302978515625 -0.9903991699218756 +0.852375 -0.452362060546875 -0.004302978515625 -0.9903991699218756 +0.8525 -0.452362060546875 -0.004302978515625 -0.9903991699218756 +0.852625 -0.450103759765625 -0.0042724609375 -0.9903991699218756 +0.85275 -0.450103759765625 -0.0042724609375 -0.9903991699218756 +0.852875 -0.447784423828125 -0.004241943359375 -0.9903991699218756 +0.853 -0.4454345703125 -0.00421142578125 -0.9903991699218756 +0.853125 -0.4454345703125 -0.00421142578125 -0.9903991699218756 +0.8532500000000001 -0.44305419921875 -0.00421142578125 -0.9903991699218756 +0.853375 -0.44305419921875 -0.00421142578125 -0.9903991699218756 +0.8535 -0.440582275390625 -0.004180908203125 -0.9903991699218756 +0.853625 -0.438079833984375 -0.004150390625 -0.9903991699218756 +0.85375 -0.438079833984375 -0.004150390625 -0.9903991699218756 +0.8538750000000001 -0.435546875 -0.004119873046875 -0.9903991699218756 +0.854 -0.435546875 -0.004119873046875 -0.9903991699218756 +0.854125 -0.432952880859375 -0.004119873046875 -0.9903991699218756 +0.8542500000000001 -0.4302978515625 -0.00408935546875 -0.9903991699218756 +0.854375 -0.4302978515625 -0.00408935546875 -0.9903991699218756 +0.8545 -0.4276123046875 -0.004058837890625 -0.9903991699218756 +0.854625 -0.4276123046875 -0.004058837890625 -0.9903991699218756 +0.85475 -0.424896240234375 -0.0040283203125 -0.9903991699218756 +0.8548750000000001 -0.422119140625 -0.003997802734375 -0.9903991699218756 +0.855 -0.422119140625 -0.003997802734375 -0.9903991699218756 +0.855125 -0.419281005859375 -0.00396728515625 -0.9903991699218756 +0.8552500000000001 -0.419281005859375 -0.00396728515625 -0.9903991699218756 +0.855375 -0.416412353515625 -0.003936767578125 -0.9903991699218756 +0.8555 -0.413482666015625 -0.003936767578125 -0.9903991699218756 +0.855625 -0.413482666015625 -0.003936767578125 -0.9903991699218756 +0.85575 -0.4105224609375 -0.00390625 -0.9903991699218756 +0.8558750000000001 -0.4105224609375 -0.00390625 -0.9903991699218756 +0.856 -0.407501220703125 -0.003875732421875 -0.9903991699218756 +0.856125 -0.404449462890625 -0.00384521484375 -0.9903991699218756 +0.8562500000000001 -0.404449462890625 -0.00384521484375 -0.9903991699218756 +0.856375 -0.401336669921875 -0.003814697265625 -0.9903991699218756 +0.8565 -0.401336669921875 -0.003814697265625 -0.9903991699218756 +0.856625 -0.398193359375 -0.0037841796875 -0.9903991699218756 +0.85675 -0.39501953125 -0.003753662109375 -0.9903991699218756 +0.8568750000000001 -0.39501953125 -0.003753662109375 -0.9903991699218756 +0.857 -0.39178466796875 -0.00372314453125 -0.9903991699218756 +0.857125 -0.39178466796875 -0.00372314453125 -0.9903991699218756 +0.8572500000000001 -0.388519287109375 -0.003692626953125 -0.9903991699218756 +0.857375 -0.38519287109375 -0.003662109375 -0.9903991699218756 +0.8575 -0.38519287109375 -0.003662109375 -0.9903991699218756 +0.857625 -0.3818359375 -0.003631591796875 -0.9903991699218756 +0.85775 -0.3818359375 -0.003631591796875 -0.9903991699218756 +0.8578750000000001 -0.378448486328125 -0.00360107421875 -0.9903991699218756 +0.858 -0.375 -0.003570556640625 -0.9903991699218756 +0.858125 -0.375 -0.003570556640625 -0.9903991699218756 +0.8582500000000001 -0.37152099609375 -0.003509521484375 -0.9903991699218756 +0.858375 -0.37152099609375 -0.003509521484375 -0.9903991699218756 +0.8585 -0.36798095703125 -0.00347900390625 -0.9903991699218756 +0.858625 -0.36444091796875 -0.003448486328125 -0.9903991699218756 +0.85875 -0.36444091796875 -0.003448486328125 -0.9903991699218756 +0.8588750000000001 -0.360809326171875 -0.00341796875 -0.9903991699218756 +0.859 -0.360809326171875 -0.00341796875 -0.9903991699218756 +0.859125 -0.357177734375 -0.003387451171875 -0.9903991699218756 +0.8592500000000001 -0.353485107421875 -0.00335693359375 -0.9903991699218756 +0.859375 -0.353485107421875 -0.00335693359375 -0.9903991699218756 +0.8595 -0.349761962890625 -0.003326416015625 -0.9903991699218756 +0.859625 -0.349761962890625 -0.003326416015625 -0.9903991699218756 +0.85975 -0.34600830078125 -0.003265380859375 -0.9903991699218756 +0.8598750000000001 -0.34222412109375 -0.00323486328125 -0.9903991699218756 +0.86 -0.34222412109375 -0.00323486328125 -0.9903991699218756 +0.860125 -0.33837890625 -0.003204345703125 -0.9903991699218756 +0.8602500000000001 -0.33837890625 -0.003204345703125 -0.9903991699218756 +0.860375 -0.334503173828125 -0.003173828125 -0.9903991699218756 +0.8605 -0.330596923828125 -0.003143310546875 -0.9903991699218756 +0.860625 -0.330596923828125 -0.003143310546875 -0.9903991699218756 +0.86075 -0.32666015625 -0.003082275390625 -0.9903991699218756 +0.8608750000000001 -0.32666015625 -0.003082275390625 -0.9903991699218756 +0.861 -0.322662353515625 -0.0030517578125 -0.9903991699218756 +0.861125 -0.31866455078125 -0.003021240234375 -0.9903991699218756 +0.8612500000000001 -0.31866455078125 -0.003021240234375 -0.9903991699218756 +0.861375 -0.314605712890625 -0.00299072265625 -0.9903991699218756 +0.8615 -0.314605712890625 -0.00299072265625 -0.9903991699218756 +0.861625 -0.310516357421875 -0.0029296875 -0.9903991699218756 +0.86175 -0.306396484375 -0.002899169921875 -0.9903991699218756 +0.8618750000000001 -0.306396484375 -0.002899169921875 -0.9903991699218756 +0.862 -0.30224609375 -0.00286865234375 -0.9903991699218756 +0.862125 -0.30224609375 -0.00286865234375 -0.9903991699218756 +0.8622500000000001 -0.298065185546875 -0.002838134765625 -0.9903991699218756 +0.862375 -0.2938232421875 -0.002777099609375 -0.9903991699218756 +0.8625 -0.2938232421875 -0.002777099609375 -0.9903991699218756 +0.862625 -0.289581298828125 -0.00274658203125 -0.9903991699218756 +0.86275 -0.289581298828125 -0.00274658203125 -0.9903991699218756 +0.8628750000000001 -0.285308837890625 -0.002716064453125 -0.9903991699218756 +0.863 -0.280975341796875 -0.002655029296875 -0.9903991699218756 +0.863125 -0.280975341796875 -0.002655029296875 -0.9903991699218756 +0.8632500000000001 -0.276641845703125 -0.00262451171875 -0.9903991699218756 +0.863375 -0.276641845703125 -0.00262451171875 -0.9903991699218756 +0.8635 -0.27227783203125 -0.0025634765625 -0.9903991699218756 +0.863625 -0.267852783203125 -0.002532958984375 -0.9903991699218756 +0.86375 -0.267852783203125 -0.002532958984375 -0.9903991699218756 +0.8638750000000001 -0.263427734375 -0.00250244140625 -0.9903991699218756 +0.864 -0.263427734375 -0.00909423828125 -0.9653259277343738 +0.864125 -0.25897216796875 -0.008941650390625 -0.9653259277343738 +0.86425 -0.25445556640625 -0.0087890625 -0.9653259277343738 +0.864375 -0.25445556640625 -0.0087890625 -0.9653259277343738 +0.8645 -0.249969482421875 -0.008636474609375 -0.9653259277343738 +0.864625 -0.249969482421875 -0.008636474609375 -0.9653259277343738 +0.86475 -0.24542236328125 -0.00848388671875 -0.9653259277343738 +0.8648750000000001 -0.2408447265625 -0.00830078125 -0.9653259277343738 +0.865 -0.2408447265625 -0.00830078125 -0.9653259277343738 +0.865125 -0.236236572265625 -0.008148193359375 -0.9653259277343738 +0.86525 -0.236236572265625 -0.008148193359375 -0.9653259277343738 +0.865375 -0.231597900390625 -0.00799560546875 -0.9653259277343738 +0.8655 -0.226959228515625 -0.007843017578125 -0.9653259277343738 +0.865625 -0.226959228515625 -0.007843017578125 -0.9653259277343738 +0.86575 -0.2222900390625 -0.007659912109375 -0.9653259277343738 +0.8658750000000001 -0.2222900390625 -0.007659912109375 -0.9653259277343738 +0.866 -0.21759033203125 -0.00750732421875 -0.9653259277343738 +0.866125 -0.212860107421875 -0.007354736328125 -0.9653259277343738 +0.86625 -0.212860107421875 -0.007354736328125 -0.9653259277343738 +0.866375 -0.208099365234375 -0.007171630859375 -0.9653259277343738 +0.8665 -0.208099365234375 -0.007171630859375 -0.9653259277343738 +0.866625 -0.203338623046875 -0.00701904296875 -0.9653259277343738 +0.86675 -0.19854736328125 -0.0068359375 -0.9653259277343738 +0.8668750000000001 -0.19854736328125 -0.0068359375 -0.9653259277343738 +0.867 -0.1937255859375 -0.006683349609375 -0.9653259277343738 +0.867125 -0.1937255859375 -0.006683349609375 -0.9653259277343738 +0.86725 -0.188873291015625 -0.00653076171875 -0.9653259277343738 +0.867375 -0.18402099609375 -0.00634765625 -0.9653259277343738 +0.8675 -0.18402099609375 -0.00634765625 -0.9653259277343738 +0.867625 -0.17913818359375 -0.00616455078125 -0.9653259277343738 +0.86775 -0.17913818359375 -0.00616455078125 -0.9653259277343738 +0.8678750000000001 -0.17425537109375 -0.006011962890625 -0.9653259277343738 +0.868 -0.169342041015625 -0.005828857421875 -0.9653259277343738 +0.868125 -0.169342041015625 -0.005828857421875 -0.9653259277343738 +0.86825 -0.164398193359375 -0.00567626953125 -0.9653259277343738 +0.868375 -0.164398193359375 -0.00567626953125 -0.9653259277343738 +0.8685 -0.159454345703125 -0.0054931640625 -0.9653259277343738 +0.868625 -0.15447998046875 -0.005340576171875 -0.9653259277343738 +0.86875 -0.15447998046875 -0.005340576171875 -0.9653259277343738 +0.8688750000000001 -0.14947509765625 -0.005157470703125 -0.9653259277343738 +0.869 -0.14947509765625 -0.005157470703125 -0.9653259277343738 +0.869125 -0.14447021484375 -0.004974365234375 -0.9653259277343738 +0.86925 -0.13946533203125 -0.00482177734375 -0.9653259277343738 +0.869375 -0.13946533203125 -0.00482177734375 -0.9653259277343738 +0.8695000000000001 -0.134429931640625 -0.004638671875 -0.9653259277343738 +0.869625 -0.134429931640625 -0.004638671875 -0.9653259277343738 +0.86975 -0.129364013671875 -0.00445556640625 -0.9653259277343738 +0.8698750000000001 -0.124298095703125 -0.0042724609375 -0.9653259277343738 +0.87 -0.124298095703125 -0.0042724609375 -0.9653259277343738 +0.870125 -0.119232177734375 -0.004119873046875 -0.9653259277343738 +0.87025 -0.119232177734375 -0.004119873046875 -0.9653259277343738 +0.870375 -0.1141357421875 -0.003936767578125 -0.9653259277343738 +0.8705000000000001 -0.109039306640625 -0.003753662109375 -0.9653259277343738 +0.870625 -0.109039306640625 -0.003753662109375 -0.9653259277343738 +0.87075 -0.103912353515625 -0.003570556640625 -0.9653259277343738 +0.8708750000000001 -0.103912353515625 -0.003570556640625 -0.9653259277343738 +0.871 -0.098785400390625 -0.003387451171875 -0.9653259277343738 +0.871125 -0.093658447265625 -0.00323486328125 -0.9653259277343738 +0.87125 -0.093658447265625 -0.00323486328125 -0.9653259277343738 +0.871375 -0.0885009765625 -0.0030517578125 -0.9653259277343738 +0.8715000000000001 -0.0885009765625 -0.0030517578125 -0.9653259277343738 +0.871625 -0.083343505859375 -0.00286865234375 -0.9653259277343738 +0.87175 -0.07818603515625 -0.002685546875 -0.9653259277343738 +0.8718750000000001 -0.07818603515625 -0.002685546875 -0.9653259277343738 +0.872 -0.072998046875 -0.00250244140625 -0.9653259277343738 +0.872125 -0.072998046875 -0.00250244140625 -0.9653259277343738 +0.87225 -0.06781005859375 -0.0023193359375 -0.9653259277343738 +0.872375 -0.0626220703125 -0.00213623046875 -0.9653259277343738 +0.8725000000000001 -0.0626220703125 -0.00213623046875 -0.9653259277343738 +0.872625 -0.05743408203125 -0.001983642578125 -0.9653259277343738 +0.87275 -0.05743408203125 -0.001983642578125 -0.9653259277343738 +0.8728750000000001 -0.05224609375 -0.001800537109375 -0.9653259277343738 +0.873 -0.047027587890625 -0.001617431640625 -0.9653259277343738 +0.873125 -0.047027587890625 -0.001617431640625 -0.9653259277343738 +0.87325 -0.04180908203125 -0.001434326171875 -0.9653259277343738 +0.873375 -0.04180908203125 -0.001434326171875 -0.9653259277343738 +0.8735000000000001 -0.036590576171875 -0.001251220703125 -0.9653259277343738 +0.873625 -0.0313720703125 -0.001068115234375 -0.9653259277343738 +0.87375 -0.0313720703125 -0.001068115234375 -0.9653259277343738 +0.8738750000000001 -0.026123046875 -0.000885009765625 -0.9653259277343738 +0.874 -0.026123046875 -0.000885009765625 -0.9653259277343738 +0.874125 -0.020904541015625 -0.000701904296875 -0.9653259277343738 +0.87425 -0.01568603515625 -0.000518798828125 -0.9653259277343738 +0.874375 -0.01568603515625 -0.000518798828125 -0.9653259277343738 +0.8745000000000001 -0.01043701171875 -0.000335693359375 -0.9653259277343738 +0.874625 -0.01043701171875 -0.000335693359375 -0.9653259277343738 +0.87475 -0.005218505859375 -0.000152587890625 -0.9653259277343738 +0.8748750000000001 0.0 0.0 -0.9653259277343738 +0.875 0.0 0.0 -0.9653259277343738 +0.875125 0.005218505859375 0.000152587890625 -0.9653259277343738 +0.87525 0.005218505859375 0.000152587890625 -0.9653259277343738 +0.875375 0.01043701171875 0.000335693359375 -0.9653259277343738 +0.8755000000000001 0.01568603515625 0.000518798828125 -0.9653259277343738 +0.875625 0.01568603515625 0.000518798828125 -0.9653259277343738 +0.87575 0.020904541015625 0.000701904296875 -0.9653259277343738 +0.8758750000000001 0.020904541015625 0.000701904296875 -0.9653259277343738 +0.876 0.026123046875 0.000885009765625 -0.9653259277343738 +0.876125 0.0313720703125 0.001068115234375 -0.9653259277343738 +0.87625 0.0313720703125 0.001068115234375 -0.9653259277343738 +0.876375 0.036590576171875 0.001251220703125 -0.9653259277343738 +0.8765000000000001 0.036590576171875 0.001251220703125 -0.9653259277343738 +0.876625 0.04180908203125 0.001434326171875 -0.9653259277343738 +0.87675 0.047027587890625 0.001617431640625 -0.9653259277343738 +0.8768750000000001 0.047027587890625 0.001617431640625 -0.9653259277343738 +0.877 0.05224609375 0.001800537109375 -0.9653259277343738 +0.877125 0.05224609375 0.001800537109375 -0.9653259277343738 +0.87725 0.05743408203125 0.001983642578125 -0.9653259277343738 +0.877375 0.0626220703125 0.00213623046875 -0.9653259277343738 +0.8775000000000001 0.0626220703125 0.00213623046875 -0.9653259277343738 +0.877625 0.06781005859375 0.0023193359375 -0.9653259277343738 +0.87775 0.06781005859375 0.0023193359375 -0.9653259277343738 +0.8778750000000001 0.072998046875 0.00250244140625 -0.9653259277343738 +0.878 0.07818603515625 0.002685546875 -0.9653259277343738 +0.878125 0.07818603515625 0.002685546875 -0.9653259277343738 +0.87825 0.083343505859375 0.00286865234375 -0.9653259277343738 +0.878375 0.083343505859375 0.00286865234375 -0.9653259277343738 +0.8785000000000001 0.0885009765625 0.0030517578125 -0.9653259277343738 +0.878625 0.093658447265625 0.00323486328125 -0.9653259277343738 +0.87875 0.093658447265625 0.00323486328125 -0.9653259277343738 +0.8788750000000001 0.098785400390625 0.003387451171875 -0.9653259277343738 +0.879 0.098785400390625 0.003387451171875 -0.9653259277343738 +0.879125 0.103912353515625 0.003570556640625 -0.9653259277343738 +0.87925 0.109039306640625 0.003753662109375 -0.9653259277343738 +0.879375 0.109039306640625 0.003753662109375 -0.9653259277343738 +0.8795000000000001 0.1141357421875 0.003936767578125 -0.9653259277343738 +0.879625 0.1141357421875 0.003936767578125 -0.9653259277343738 +0.87975 0.119232177734375 0.004119873046875 -0.9653259277343738 +0.8798750000000001 0.124298095703125 0.0042724609375 -0.9653259277343738 +0.88 0.124298095703125 0.0042724609375 -0.9653259277343738 +0.880125 0.129364013671875 0.00445556640625 -0.9653259277343738 +0.88025 0.129364013671875 0.00445556640625 -0.9653259277343738 +0.880375 0.134429931640625 0.004638671875 -0.9653259277343738 +0.8805000000000001 0.13946533203125 0.00482177734375 -0.9653259277343738 +0.880625 0.13946533203125 0.00482177734375 -0.9653259277343738 +0.88075 0.14447021484375 0.004974365234375 -0.9653259277343738 +0.880875 0.14447021484375 0.004974365234375 -0.9653259277343738 +0.881 0.14947509765625 0.005157470703125 -0.9653259277343738 +0.881125 0.15447998046875 0.005340576171875 -0.9653259277343738 +0.88125 0.15447998046875 0.005340576171875 -0.9653259277343738 +0.881375 0.159454345703125 0.0054931640625 -0.9653259277343738 +0.8815000000000001 0.159454345703125 0.0054931640625 -0.9653259277343738 +0.881625 0.164398193359375 0.00567626953125 -0.9653259277343738 +0.88175 0.169342041015625 0.005828857421875 -0.9653259277343738 +0.881875 0.169342041015625 0.005828857421875 -0.9653259277343738 +0.882 0.17425537109375 0.006011962890625 -0.9653259277343738 +0.882125 0.17425537109375 0.006011962890625 -0.9653259277343738 +0.88225 0.17913818359375 0.00616455078125 -0.9653259277343738 +0.882375 0.18402099609375 0.00634765625 -0.9653259277343738 +0.8825000000000001 0.18402099609375 0.00634765625 -0.9653259277343738 +0.882625 0.188873291015625 0.00653076171875 -0.9653259277343738 +0.88275 0.188873291015625 0.00653076171875 -0.9653259277343738 +0.882875 0.1937255859375 0.006683349609375 -0.9653259277343738 +0.883 0.19854736328125 0.0068359375 -0.9653259277343738 +0.883125 0.19854736328125 0.0068359375 -0.9653259277343738 +0.88325 0.203338623046875 0.00701904296875 -0.9653259277343738 +0.883375 0.203338623046875 0.00701904296875 -0.9653259277343738 +0.8835000000000001 0.208099365234375 0.007171630859375 -0.9653259277343738 +0.883625 0.212860107421875 0.007354736328125 -0.9653259277343738 +0.88375 0.212860107421875 0.007354736328125 -0.9653259277343738 +0.883875 0.21759033203125 0.00750732421875 -0.9653259277343738 +0.884 0.21759033203125 0.00750732421875 -0.9653259277343738 +0.884125 0.2222900390625 0.007659912109375 -0.9653259277343738 +0.88425 0.226959228515625 0.007843017578125 -0.9653259277343738 +0.884375 0.226959228515625 0.007843017578125 -0.9653259277343738 +0.8845000000000001 0.231597900390625 0.00799560546875 -0.9653259277343738 +0.884625 0.231597900390625 0.00799560546875 -0.9653259277343738 +0.88475 0.236236572265625 0.008148193359375 -0.9653259277343738 +0.884875 0.2408447265625 0.00830078125 -0.9653259277343738 +0.885 0.2408447265625 0.00830078125 -0.9653259277343738 +0.8851250000000001 0.24542236328125 0.00848388671875 -0.9653259277343738 +0.88525 0.24542236328125 0.00848388671875 -0.9653259277343738 +0.885375 0.249969482421875 0.008636474609375 -0.9653259277343738 +0.8855000000000001 0.25445556640625 0.0087890625 -0.9653259277343738 +0.885625 0.25445556640625 0.0087890625 -0.9653259277343738 +0.88575 0.25897216796875 0.008941650390625 -0.9653259277343738 +0.885875 0.25897216796875 0.008941650390625 -0.9653259277343738 +0.886 0.263427734375 0.00909423828125 -0.9653259277343738 +0.8861250000000001 0.267852783203125 0.009246826171875 -0.9653259277343738 +0.88625 0.267852783203125 0.009246826171875 -0.9653259277343738 +0.886375 0.27227783203125 0.0093994140625 -0.9653259277343738 +0.8865000000000001 0.27227783203125 0.0093994140625 -0.9653259277343738 +0.886625 0.276641845703125 0.009552001953125 -0.9653259277343738 +0.88675 0.280975341796875 0.00970458984375 -0.9653259277343738 +0.886875 0.280975341796875 0.00970458984375 -0.9653259277343738 +0.887 0.285308837890625 0.009857177734375 -0.9653259277343738 +0.8871250000000001 0.285308837890625 0.009857177734375 -0.9653259277343738 +0.88725 0.289581298828125 0.010009765625 -0.9653259277343738 +0.887375 0.2938232421875 0.0101318359375 -0.9653259277343738 +0.8875000000000001 0.2938232421875 0.0101318359375 -0.9653259277343738 +0.887625 0.298065185546875 0.010284423828125 -0.9653259277343738 +0.88775 0.298065185546875 0.010284423828125 -0.9653259277343738 +0.887875 0.30224609375 0.01043701171875 -0.9653259277343738 +0.888 0.306396484375 0.010589599609375 -0.9653259277343738 +0.8881250000000001 0.306396484375 0.010589599609375 -0.9653259277343738 +0.88825 0.310516357421875 0.010711669921875 -0.9653259277343738 +0.888375 0.310516357421875 0.010711669921875 -0.9653259277343738 +0.8885000000000001 0.314605712890625 0.0108642578125 -0.9653259277343738 +0.888625 0.31866455078125 0.011016845703125 -0.9653259277343738 +0.88875 0.31866455078125 0.011016845703125 -0.9653259277343738 +0.888875 0.322662353515625 0.011138916015625 -0.9653259277343738 +0.889 0.322662353515625 0.011138916015625 -0.9653259277343738 +0.8891250000000001 0.32666015625 0.01129150390625 -0.9653259277343738 +0.88925 0.330596923828125 0.01141357421875 -0.9653259277343738 +0.889375 0.330596923828125 0.01141357421875 -0.9653259277343738 +0.8895000000000001 0.334503173828125 0.011566162109375 -0.9653259277343738 +0.889625 0.334503173828125 0.011566162109375 -0.9653259277343738 +0.88975 0.33837890625 0.011688232421875 -0.9653259277343738 +0.889875 0.34222412109375 0.011810302734375 -0.9653259277343738 +0.89 0.34222412109375 0.011810302734375 -0.9653259277343738 +0.8901250000000001 0.34600830078125 0.011962890625 -0.9653259277343738 +0.89025 0.34600830078125 0.011962890625 -0.9653259277343738 +0.890375 0.349761962890625 0.0120849609375 -0.9653259277343738 +0.8905000000000001 0.353485107421875 0.01220703125 -0.9653259277343738 +0.890625 0.353485107421875 0.01220703125 -0.9653259277343738 +0.89075 0.357177734375 0.0123291015625 -0.9653259277343738 +0.890875 0.357177734375 0.0123291015625 -0.9653259277343738 +0.891 0.360809326171875 0.012451171875 -0.9653259277343738 +0.8911250000000001 0.36444091796875 0.0125732421875 -0.9653259277343738 +0.89125 0.36444091796875 0.0125732421875 -0.9653259277343738 +0.891375 0.36798095703125 0.0126953125 -0.9653259277343738 +0.8915000000000001 0.36798095703125 0.0126953125 -0.9653259277343738 +0.891625 0.37152099609375 0.0128173828125 -0.9653259277343738 +0.89175 0.375 0.012939453125 -0.9653259277343738 +0.891875 0.375 0.012939453125 -0.9653259277343738 +0.892 0.378448486328125 0.0130615234375 -0.9653259277343738 +0.8921250000000001 0.378448486328125 0.0130615234375 -0.9653259277343738 +0.89225 0.3818359375 0.01318359375 -0.9653259277343738 +0.892375 0.38519287109375 0.0133056640625 -0.9653259277343738 +0.8925000000000001 0.38519287109375 0.0133056640625 -0.9653259277343738 +0.892625 0.388519287109375 0.013427734375 -0.9653259277343738 +0.89275 0.388519287109375 0.013427734375 -0.9653259277343738 +0.892875 0.39178466796875 0.013519287109375 -0.9653259277343738 +0.893 0.39501953125 0.013641357421875 -0.9653259277343738 +0.8931250000000001 0.39501953125 0.013641357421875 -0.9653259277343738 +0.89325 0.398193359375 0.013763427734375 -0.9653259277343738 +0.893375 0.398193359375 0.013763427734375 -0.9653259277343738 +0.8935000000000001 0.401336669921875 0.01385498046875 -0.9653259277343738 +0.893625 0.404449462890625 0.01397705078125 -0.9653259277343738 +0.89375 0.404449462890625 0.01397705078125 -0.9653259277343738 +0.893875 0.407501220703125 0.014068603515625 -0.9653259277343738 +0.894 0.407501220703125 0.014068603515625 -0.9653259277343738 +0.8941250000000001 0.4105224609375 0.014190673828125 -0.9653259277343738 +0.89425 0.413482666015625 0.0142822265625 -0.9653259277343738 +0.894375 0.413482666015625 0.0142822265625 -0.9653259277343738 +0.8945000000000001 0.416412353515625 0.014373779296875 -0.9653259277343738 +0.894625 0.416412353515625 0.014373779296875 -0.9653259277343738 +0.89475 0.419281005859375 0.014495849609375 -0.9653259277343738 +0.894875 0.422119140625 0.01458740234375 -0.9653259277343738 +0.895 0.422119140625 0.01458740234375 -0.9653259277343738 +0.8951250000000001 0.424896240234375 0.014678955078125 -0.9653259277343738 +0.89525 0.424896240234375 0.014678955078125 -0.9653259277343738 +0.895375 0.4276123046875 0.0147705078125 -0.9653259277343738 +0.8955000000000001 0.4302978515625 0.014862060546875 -0.9653259277343738 +0.895625 0.4302978515625 0.014862060546875 -0.9653259277343738 +0.89575 0.432952880859375 0.01495361328125 -0.9653259277343738 +0.895875 0.432952880859375 0.01495361328125 -0.9653259277343738 +0.896 0.435546875 0.093048095703125 -0.7862487792968719 +0.8961250000000001 0.438079833984375 0.093597412109375 -0.7862487792968719 +0.89625 0.438079833984375 0.093597412109375 -0.7862487792968719 +0.896375 0.440582275390625 0.0941162109375 -0.7862487792968719 +0.8965 0.440582275390625 0.0941162109375 -0.7862487792968719 +0.896625 0.44305419921875 0.09466552734375 -0.7862487792968719 +0.89675 0.4454345703125 0.09515380859375 -0.7862487792968719 +0.896875 0.4454345703125 0.09515380859375 -0.7862487792968719 +0.897 0.447784423828125 0.095672607421875 -0.7862487792968719 +0.8971250000000001 0.447784423828125 0.095672607421875 -0.7862487792968719 +0.89725 0.450103759765625 0.096160888671875 -0.7862487792968719 +0.897375 0.452362060546875 0.096649169921875 -0.7862487792968719 +0.8975 0.452362060546875 0.096649169921875 -0.7862487792968719 +0.897625 0.454559326171875 0.09710693359375 -0.7862487792968719 +0.89775 0.454559326171875 0.09710693359375 -0.7862487792968719 +0.897875 0.45672607421875 0.097564697265625 -0.7862487792968719 +0.898 0.458831787109375 0.0980224609375 -0.7862487792968719 +0.8981250000000001 0.458831787109375 0.0980224609375 -0.7862487792968719 +0.89825 0.46087646484375 0.09844970703125 -0.7862487792968719 +0.898375 0.46087646484375 0.09844970703125 -0.7862487792968719 +0.8985 0.462890625 0.098876953125 -0.7862487792968719 +0.898625 0.464813232421875 0.09930419921875 -0.7862487792968719 +0.89875 0.464813232421875 0.09930419921875 -0.7862487792968719 +0.898875 0.46673583984375 0.099700927734375 -0.7862487792968719 +0.899 0.46673583984375 0.099700927734375 -0.7862487792968719 +0.8991250000000001 0.46856689453125 0.10009765625 -0.7862487792968719 +0.89925 0.470367431640625 0.100494384765625 -0.7862487792968719 +0.899375 0.470367431640625 0.100494384765625 -0.7862487792968719 +0.8995 0.472137451171875 0.100860595703125 -0.7862487792968719 +0.899625 0.472137451171875 0.100860595703125 -0.7862487792968719 +0.89975 0.47381591796875 0.101226806640625 -0.7862487792968719 +0.899875 0.4754638671875 0.101593017578125 -0.7862487792968719 +0.9 0.4754638671875 0.101593017578125 -0.7862487792968719 +0.9001250000000001 0.47705078125 0.1019287109375 -0.7862487792968719 +0.90025 0.47705078125 0.1019287109375 -0.7862487792968719 +0.900375 0.478607177734375 0.10223388671875 -0.7862487792968719 +0.9005 0.480072021484375 0.102569580078125 -0.7862487792968719 +0.900625 0.480072021484375 0.102569580078125 -0.7862487792968719 +0.90075 0.48150634765625 0.102874755859375 -0.7862487792968719 +0.900875 0.48150634765625 0.102874755859375 -0.7862487792968719 +0.901 0.48291015625 0.103179931640625 -0.7862487792968719 +0.9011250000000000768 0.484222412109375 0.10345458984375 -0.7862487792968719 +0.90125 0.484222412109375 0.10345458984375 -0.7862487792968719 +0.901375 0.485504150390625 0.103729248046875 -0.7862487792968719 +0.9015 0.485504150390625 0.103729248046875 -0.7862487792968719 +0.901625 0.486724853515625 0.103973388671875 -0.7862487792968719 +0.90175 0.487884521484375 0.104248046875 -0.7862487792968719 +0.901875 0.487884521484375 0.104248046875 -0.7862487792968719 +0.902 0.489013671875 0.104461669921875 -0.7862487792968719 +0.9021250000000000768 0.489013671875 0.104461669921875 -0.7862487792968719 +0.90225 0.490081787109375 0.104705810546875 -0.7862487792968719 +0.902375 0.4910888671875 0.10491943359375 -0.7862487792968719 +0.9025 0.4910888671875 0.10491943359375 -0.7862487792968719 +0.902625 0.492034912109375 0.105133056640625 -0.7862487792968719 +0.90275 0.492034912109375 0.105133056640625 -0.7862487792968719 +0.902875 0.492950439453125 0.105316162109375 -0.7862487792968719 +0.903 0.4937744140625 0.105499267578125 -0.7862487792968719 +0.9031250000000000768 0.4937744140625 0.105499267578125 -0.7862487792968719 +0.90325 0.49456787109375 0.10565185546875 -0.7862487792968719 +0.903375 0.49456787109375 0.10565185546875 -0.7862487792968719 +0.9035 0.49530029296875 0.105804443359375 -0.7862487792968719 +0.903625 0.496002197265625 0.10595703125 -0.7862487792968719 +0.90375 0.496002197265625 0.10595703125 -0.7862487792968719 +0.903875 0.496612548828125 0.106109619140625 -0.7862487792968719 +0.904 0.496612548828125 0.106109619140625 -0.7862487792968719 +0.9041250000000000768 0.4971923828125 0.106231689453125 -0.7862487792968719 +0.90425 0.497711181640625 0.1063232421875 -0.7862487792968719 +0.904375 0.497711181640625 0.1063232421875 -0.7862487792968719 +0.9045 0.498199462890625 0.1064453125 -0.7862487792968719 +0.904625 0.498199462890625 0.1064453125 -0.7862487792968719 +0.90475 0.49859619140625 0.10650634765625 -0.7862487792968719 +0.904875 0.49896240234375 0.106597900390625 -0.7862487792968719 +0.905 0.49896240234375 0.106597900390625 -0.7862487792968719 +0.9051250000000000768 0.499267578125 0.106658935546875 -0.7862487792968719 +0.90525 0.499267578125 0.106658935546875 -0.7862487792968719 +0.905375 0.49951171875 0.106719970703125 -0.7862487792968719 +0.9055 0.49969482421875 0.10675048828125 -0.7862487792968719 +0.905625 0.49969482421875 0.10675048828125 -0.7862487792968719 +0.90575 0.49981689453125 0.106781005859375 -0.7862487792968719 +0.905875 0.49981689453125 0.106781005859375 -0.7862487792968719 +0.906 0.499908447265625 0.1068115234375 -0.7862487792968719 +0.9061250000000000768 0.49993896484375 0.1068115234375 -0.7862487792968719 +0.90625 0.49993896484375 0.1068115234375 -0.7862487792968719 +0.906375 0.499908447265625 0.1068115234375 -0.7862487792968719 +0.9065 0.499908447265625 0.1068115234375 -0.7862487792968719 +0.906625 0.49981689453125 0.106781005859375 -0.7862487792968719 +0.9067500000000000768 0.49969482421875 0.10675048828125 -0.7862487792968719 +0.906875 0.49969482421875 0.10675048828125 -0.7862487792968719 +0.907 0.49951171875 0.106719970703125 -0.7862487792968719 +0.9071250000000000768 0.49951171875 0.106719970703125 -0.7862487792968719 +0.90725 0.499267578125 0.106658935546875 -0.7862487792968719 +0.907375 0.49896240234375 0.106597900390625 -0.7862487792968719 +0.9075 0.49896240234375 0.106597900390625 -0.7862487792968719 +0.907625 0.49859619140625 0.10650634765625 -0.7862487792968719 +0.9077500000000000768 0.49859619140625 0.10650634765625 -0.7862487792968719 +0.907875 0.498199462890625 0.1064453125 -0.7862487792968719 +0.908 0.497711181640625 0.1063232421875 -0.7862487792968719 +0.9081250000000000768 0.497711181640625 0.1063232421875 -0.7862487792968719 +0.90825 0.4971923828125 0.106231689453125 -0.7862487792968719 +0.908375 0.4971923828125 0.106231689453125 -0.7862487792968719 +0.9085 0.496612548828125 0.106109619140625 -0.7862487792968719 +0.908625 0.496002197265625 0.10595703125 -0.7862487792968719 +0.9087500000000000768 0.496002197265625 0.10595703125 -0.7862487792968719 +0.908875 0.49530029296875 0.105804443359375 -0.7862487792968719 +0.909 0.49530029296875 0.105804443359375 -0.7862487792968719 +0.9091250000000000768 0.49456787109375 0.10565185546875 -0.7862487792968719 +0.90925 0.4937744140625 0.105499267578125 -0.7862487792968719 +0.909375 0.4937744140625 0.105499267578125 -0.7862487792968719 +0.9095 0.492950439453125 0.105316162109375 -0.7862487792968719 +0.909625 0.492950439453125 0.105316162109375 -0.7862487792968719 +0.9097500000000000768 0.492034912109375 0.105133056640625 -0.7862487792968719 +0.909875 0.4910888671875 0.10491943359375 -0.7862487792968719 +0.91 0.4910888671875 0.10491943359375 -0.7862487792968719 +0.910125000000000096 0.490081787109375 0.104705810546875 -0.7862487792968719 +0.91025 0.490081787109375 0.104705810546875 -0.7862487792968719 +0.910375 0.489013671875 0.104461669921875 -0.7862487792968719 +0.9105 0.487884521484375 0.104248046875 -0.7862487792968719 +0.910625 0.487884521484375 0.104248046875 -0.7862487792968719 +0.9107500000000000768 0.486724853515625 0.103973388671875 -0.7862487792968719 +0.910875 0.486724853515625 0.103973388671875 -0.7862487792968719 +0.911 0.485504150390625 0.103729248046875 -0.7862487792968719 +0.911125000000000096 0.484222412109375 0.10345458984375 -0.7862487792968719 +0.91125 0.484222412109375 0.10345458984375 -0.7862487792968719 +0.911375 0.48291015625 0.103179931640625 -0.7862487792968719 +0.9115 0.48291015625 0.103179931640625 -0.7862487792968719 +0.911625 0.48150634765625 0.102874755859375 -0.7862487792968719 +0.9117500000000000768 0.480072021484375 0.102569580078125 -0.7862487792968719 +0.911875 0.480072021484375 0.102569580078125 -0.7862487792968719 +0.912 0.478607177734375 0.10223388671875 -0.7862487792968719 +0.912125 0.478607177734375 0.10223388671875 -0.7862487792968719 +0.91225 0.47705078125 0.1019287109375 -0.7862487792968719 +0.912375 0.4754638671875 0.101593017578125 -0.7862487792968719 +0.9125 0.4754638671875 0.101593017578125 -0.7862487792968719 +0.912625 0.47381591796875 0.101226806640625 -0.7862487792968719 +0.9127500000000000768 0.47381591796875 0.101226806640625 -0.7862487792968719 +0.912875 0.472137451171875 0.100860595703125 -0.7862487792968719 +0.913 0.470367431640625 0.100494384765625 -0.7862487792968719 +0.913125 0.470367431640625 0.100494384765625 -0.7862487792968719 +0.91325 0.46856689453125 0.10009765625 -0.7862487792968719 +0.913375 0.46856689453125 0.10009765625 -0.7862487792968719 +0.9135 0.46673583984375 0.099700927734375 -0.7862487792968719 +0.913625 0.464813232421875 0.09930419921875 -0.7862487792968719 +0.9137500000000000768 0.464813232421875 0.09930419921875 -0.7862487792968719 +0.913875 0.462890625 0.098876953125 -0.7862487792968719 +0.914 0.462890625 0.098876953125 -0.7862487792968719 +0.914125 0.46087646484375 0.09844970703125 -0.7862487792968719 +0.91425 0.458831787109375 0.0980224609375 -0.7862487792968719 +0.914375 0.458831787109375 0.0980224609375 -0.7862487792968719 +0.9145 0.45672607421875 0.097564697265625 -0.7862487792968719 +0.914625 0.45672607421875 0.097564697265625 -0.7862487792968719 +0.9147500000000000768 0.454559326171875 0.09710693359375 -0.7862487792968719 +0.914875 0.452362060546875 0.096649169921875 -0.7862487792968719 +0.915 0.452362060546875 0.096649169921875 -0.7862487792968719 +0.915125 0.450103759765625 0.096160888671875 -0.7862487792968719 +0.91525 0.450103759765625 0.096160888671875 -0.7862487792968719 +0.915375 0.447784423828125 0.095672607421875 -0.7862487792968719 +0.9155 0.4454345703125 0.09515380859375 -0.7862487792968719 +0.915625 0.4454345703125 0.09515380859375 -0.7862487792968719 +0.9157500000000000768 0.44305419921875 0.09466552734375 -0.7862487792968719 +0.915875 0.44305419921875 0.09466552734375 -0.7862487792968719 +0.916 0.440582275390625 0.0941162109375 -0.7862487792968719 +0.916125 0.438079833984375 0.093597412109375 -0.7862487792968719 +0.91625 0.438079833984375 0.093597412109375 -0.7862487792968719 +0.916375 0.435546875 0.093048095703125 -0.7862487792968719 +0.9165 0.435546875 0.093048095703125 -0.7862487792968719 +0.916625 0.432952880859375 0.092498779296875 -0.7862487792968719 +0.9167500000000000768 0.4302978515625 0.0919189453125 -0.7862487792968719 +0.916875 0.4302978515625 0.0919189453125 -0.7862487792968719 +0.917 0.4276123046875 0.09136962890625 -0.7862487792968719 +0.917125 0.4276123046875 0.09136962890625 -0.7862487792968719 +0.91725 0.424896240234375 0.09075927734375 -0.7862487792968719 +0.917375 0.422119140625 0.090179443359375 -0.7862487792968719 +0.9175 0.422119140625 0.090179443359375 -0.7862487792968719 +0.917625 0.419281005859375 0.089569091796875 -0.7862487792968719 +0.9177500000000000768 0.419281005859375 0.089569091796875 -0.7862487792968719 +0.917875 0.416412353515625 0.088958740234375 -0.7862487792968719 +0.918 0.413482666015625 0.088348388671875 -0.7862487792968719 +0.918125 0.413482666015625 0.088348388671875 -0.7862487792968719 +0.91825 0.4105224609375 0.08770751953125 -0.7862487792968719 +0.918375 0.4105224609375 0.08770751953125 -0.7862487792968719 +0.9185 0.407501220703125 0.087066650390625 -0.7862487792968719 +0.918625 0.404449462890625 0.086395263671875 -0.7862487792968719 +0.9187500000000000768 0.404449462890625 0.086395263671875 -0.7862487792968719 +0.918875 0.401336669921875 0.08575439453125 -0.7862487792968719 +0.919 0.401336669921875 0.08575439453125 -0.7862487792968719 +0.919125 0.398193359375 0.0850830078125 -0.7862487792968719 +0.91925 0.39501953125 0.084381103515625 -0.7862487792968719 +0.919375 0.39501953125 0.084381103515625 -0.7862487792968719 +0.9195 0.39178466796875 0.083709716796875 -0.7862487792968719 +0.919625 0.39178466796875 0.083709716796875 -0.7862487792968719 +0.9197500000000000768 0.388519287109375 0.0830078125 -0.7862487792968719 +0.919875 0.38519287109375 0.082305908203125 -0.7862487792968719 +0.92 0.38519287109375 0.082305908203125 -0.7862487792968719 +0.920125 0.3818359375 0.081573486328125 -0.7862487792968719 +0.92025 0.3818359375 0.081573486328125 -0.7862487792968719 +0.920375 0.378448486328125 0.080841064453125 -0.7862487792968719 +0.9205 0.375 0.080108642578125 -0.7862487792968719 +0.920625 0.375 0.080108642578125 -0.7862487792968719 +0.9207500000000000768 0.37152099609375 0.079376220703125 -0.7862487792968719 +0.920875 0.37152099609375 0.079376220703125 -0.7862487792968719 +0.921 0.36798095703125 0.07861328125 -0.7862487792968719 +0.921125 0.36444091796875 0.077850341796875 -0.7862487792968719 +0.92125 0.36444091796875 0.077850341796875 -0.7862487792968719 +0.921375 0.360809326171875 0.07708740234375 -0.7862487792968719 +0.9215 0.360809326171875 0.07708740234375 -0.7862487792968719 +0.921625 0.357177734375 0.0762939453125 -0.7862487792968719 +0.9217500000000000768 0.353485107421875 0.075531005859375 -0.7862487792968719 +0.921875 0.353485107421875 0.075531005859375 -0.7862487792968719 +0.922 0.349761962890625 0.074737548828125 -0.7862487792968719 +0.922125 0.349761962890625 0.074737548828125 -0.7862487792968719 +0.92225 0.34600830078125 0.07391357421875 -0.7862487792968719 +0.922375000000000096 0.34222412109375 0.0731201171875 -0.7862487792968719 +0.9225 0.34222412109375 0.0731201171875 -0.7862487792968719 +0.922625 0.33837890625 0.072296142578125 -0.7862487792968719 +0.922750000000000096 0.33837890625 0.072296142578125 -0.7862487792968719 +0.922875 0.334503173828125 0.07147216796875 -0.7862487792968719 +0.923 0.330596923828125 0.07061767578125 -0.7862487792968719 +0.923125 0.330596923828125 0.07061767578125 -0.7862487792968719 +0.92325 0.32666015625 0.069793701171875 -0.7862487792968719 +0.923375000000000096 0.32666015625 0.069793701171875 -0.7862487792968719 +0.9235 0.322662353515625 0.068939208984375 -0.7862487792968719 +0.923625 0.31866455078125 0.068084716796875 -0.7862487792968719 +0.923750000000000096 0.31866455078125 0.068084716796875 -0.7862487792968719 +0.923875 0.314605712890625 0.06719970703125 -0.7862487792968719 +0.924 0.314605712890625 0.06719970703125 -0.7862487792968719 +0.924125 0.310516357421875 0.06634521484375 -0.7862487792968719 +0.92425 0.306396484375 0.065460205078125 -0.7862487792968719 +0.924375000000000096 0.306396484375 0.065460205078125 -0.7862487792968719 +0.9245 0.30224609375 0.0645751953125 -0.7862487792968719 +0.924625 0.30224609375 0.0645751953125 -0.7862487792968719 +0.924750000000000096 0.298065185546875 0.06365966796875 -0.7862487792968719 +0.924875 0.2938232421875 0.062774658203125 -0.7862487792968719 +0.925 0.2938232421875 0.062774658203125 -0.7862487792968719 +0.925125 0.289581298828125 0.061859130859375 -0.7862487792968719 +0.92525 0.289581298828125 0.061859130859375 -0.7862487792968719 +0.925375000000000096 0.285308837890625 0.060943603515625 -0.7862487792968719 +0.9255 0.280975341796875 0.060028076171875 -0.7862487792968719 +0.925625 0.280975341796875 0.060028076171875 -0.7862487792968719 +0.925750000000000096 0.276641845703125 0.05908203125 -0.7862487792968719 +0.925875 0.276641845703125 0.05908203125 -0.7862487792968719 +0.926 0.27227783203125 0.05816650390625 -0.7862487792968719 +0.926125 0.267852783203125 0.057220458984375 -0.7862487792968719 +0.92625 0.267852783203125 0.057220458984375 -0.7862487792968719 +0.926375000000000096 0.263427734375 0.0562744140625 -0.7862487792968719 +0.9265 0.263427734375 0.0562744140625 -0.7862487792968719 +0.926625 0.25897216796875 0.055328369140625 -0.7862487792968719 +0.926750000000000096 0.25445556640625 0.054351806640625 -0.7862487792968719 +0.926875 0.25445556640625 0.054351806640625 -0.7862487792968719 +0.927 0.249969482421875 0.05340576171875 -0.7862487792968719 +0.927125 0.249969482421875 0.05340576171875 -0.7862487792968719 +0.92725 0.24542236328125 0.05242919921875 -0.7862487792968719 +0.927375000000000096 0.2408447265625 0.05145263671875 -0.7862487792968719 +0.9275 0.2408447265625 0.05145263671875 -0.7862487792968719 +0.927625 0.236236572265625 0.05047607421875 -0.7862487792968719 +0.927750000000000096 0.236236572265625 0.05047607421875 -0.7862487792968719 +0.927875 0.231597900390625 0.049468994140625 -0.7862487792968719 +0.928 0.226959228515625 0.11761474609375 -0.481719970703120832 +0.928125 0.226959228515625 0.11761474609375 -0.481719970703120832 +0.92825 0.2222900390625 0.11517333984375 -0.481719970703120832 +0.928375000000000096 0.2222900390625 0.11517333984375 -0.481719970703120832 +0.9285 0.21759033203125 0.11273193359375 -0.481719970703120832 +0.928625 0.212860107421875 0.11029052734375 -0.481719970703120832 +0.92875 0.212860107421875 0.11029052734375 -0.481719970703120832 +0.928875 0.208099365234375 0.10784912109375 -0.481719970703120832 +0.929 0.208099365234375 0.10784912109375 -0.481719970703120832 +0.929125 0.203338623046875 0.1053466796875 -0.481719970703120832 +0.92925 0.19854736328125 0.102874755859375 -0.481719970703120832 +0.929375000000000096 0.19854736328125 0.102874755859375 -0.481719970703120832 +0.9295 0.1937255859375 0.100372314453125 -0.481719970703120832 +0.929625 0.1937255859375 0.100372314453125 -0.481719970703120832 +0.92975 0.188873291015625 0.097869873046875 -0.481719970703120832 +0.929875 0.18402099609375 0.095367431640625 -0.481719970703120832 +0.93 0.18402099609375 0.095367431640625 -0.481719970703120832 +0.930125 0.17913818359375 0.09283447265625 -0.481719970703120832 +0.93025 0.17913818359375 0.09283447265625 -0.481719970703120832 +0.930375000000000096 0.17425537109375 0.090301513671875 -0.481719970703120832 +0.9305 0.169342041015625 0.087738037109375 -0.481719970703120832 +0.930625 0.169342041015625 0.087738037109375 -0.481719970703120832 +0.93075 0.164398193359375 0.085174560546875 -0.481719970703120832 +0.930875 0.164398193359375 0.085174560546875 -0.481719970703120832 +0.931 0.159454345703125 0.082611083984375 -0.481719970703120832 +0.931125 0.15447998046875 0.080047607421875 -0.481719970703120832 +0.93125 0.15447998046875 0.080047607421875 -0.481719970703120832 +0.931375000000000096 0.14947509765625 0.07745361328125 -0.481719970703120832 +0.9315 0.14947509765625 0.07745361328125 -0.481719970703120832 +0.931625 0.14447021484375 0.074859619140625 -0.481719970703120832 +0.93175 0.13946533203125 0.072265625 -0.481719970703120832 +0.931875 0.13946533203125 0.072265625 -0.481719970703120832 +0.932 0.134429931640625 0.06964111328125 -0.481719970703120832 +0.932125 0.134429931640625 0.06964111328125 -0.481719970703120832 +0.93225 0.129364013671875 0.067047119140625 -0.481719970703120832 +0.932375000000000096 0.124298095703125 0.064422607421875 -0.481719970703120832 +0.9325 0.124298095703125 0.064422607421875 -0.481719970703120832 +0.932625 0.119232177734375 0.061798095703125 -0.481719970703120832 +0.93275 0.119232177734375 0.061798095703125 -0.481719970703120832 +0.932875 0.1141357421875 0.05914306640625 -0.481719970703120832 +0.933 0.109039306640625 0.056488037109375 -0.481719970703120832 +0.933125 0.109039306640625 0.056488037109375 -0.481719970703120832 +0.93325 0.103912353515625 0.0538330078125 -0.481719970703120832 +0.933375000000000096 0.103912353515625 0.0538330078125 -0.481719970703120832 +0.9335 0.098785400390625 0.051177978515625 -0.481719970703120832 +0.933625 0.093658447265625 0.04852294921875 -0.481719970703120832 +0.93375 0.093658447265625 0.04852294921875 -0.481719970703120832 +0.933875 0.0885009765625 0.045867919921875 -0.481719970703120832 +0.934 0.0885009765625 0.045867919921875 -0.481719970703120832 +0.934125 0.083343505859375 0.043182373046875 -0.481719970703120832 +0.93425 0.07818603515625 0.040496826171875 -0.481719970703120832 +0.934375000000000096 0.07818603515625 0.040496826171875 -0.481719970703120832 +0.9345 0.072998046875 0.037841796875 -0.481719970703120832 +0.934625 0.072998046875 0.037841796875 -0.481719970703120832 +0.93475 0.06781005859375 0.035125732421875 -0.481719970703120832 +0.934875 0.0626220703125 0.032440185546875 -0.481719970703120832 +0.935 0.0626220703125 0.032440185546875 -0.481719970703120832 +0.935125 0.05743408203125 0.029754638671875 -0.481719970703120832 +0.93525 0.05743408203125 0.029754638671875 -0.481719970703120832 +0.935375000000000096 0.05224609375 0.027069091796875 -0.481719970703120832 +0.9355 0.047027587890625 0.02435302734375 -0.481719970703120832 +0.935625 0.047027587890625 0.02435302734375 -0.481719970703120832 +0.93575 0.04180908203125 0.02166748046875 -0.481719970703120832 +0.935875 0.04180908203125 0.02166748046875 -0.481719970703120832 +0.936 0.036590576171875 0.018951416015625 -0.481719970703120832 +0.936125 0.0313720703125 0.0162353515625 -0.481719970703120832 +0.93625 0.0313720703125 0.0162353515625 -0.481719970703120832 +0.936375000000000096 0.026123046875 0.0135498046875 -0.481719970703120832 +0.9365 0.026123046875 0.0135498046875 -0.481719970703120832 +0.936625 0.020904541015625 0.010833740234375 -0.481719970703120832 +0.93675 0.01568603515625 0.00811767578125 -0.481719970703120832 +0.936875 0.01568603515625 0.00811767578125 -0.481719970703120832 +0.937 0.01043701171875 0.005401611328125 -0.481719970703120832 +0.937125 0.01043701171875 0.005401611328125 -0.481719970703120832 +0.93725 0.005218505859375 0.002685546875 -0.481719970703120832 +0.937375000000000096 0.0 0.0 -0.481719970703120832 +0.9375 0.0 0.0 -0.481719970703120832 +0.937625 -0.005218505859375 -0.002685546875 -0.481719970703120832 +0.93775 -0.005218505859375 -0.002685546875 -0.481719970703120832 +0.937875 -0.01043701171875 -0.005401611328125 -0.481719970703120832 +0.938000000000000096 -0.01568603515625 -0.00811767578125 -0.481719970703120832 +0.938125 -0.01568603515625 -0.00811767578125 -0.481719970703120832 +0.93825 -0.020904541015625 -0.010833740234375 -0.481719970703120832 +0.938375000000000096 -0.020904541015625 -0.010833740234375 -0.481719970703120832 +0.9385 -0.026123046875 -0.0135498046875 -0.481719970703120832 +0.938625 -0.0313720703125 -0.0162353515625 -0.481719970703120832 +0.93875 -0.0313720703125 -0.0162353515625 -0.481719970703120832 +0.938875 -0.036590576171875 -0.018951416015625 -0.481719970703120832 +0.939000000000000096 -0.036590576171875 -0.018951416015625 -0.481719970703120832 +0.939125 -0.04180908203125 -0.02166748046875 -0.481719970703120832 +0.93925 -0.047027587890625 -0.02435302734375 -0.481719970703120832 +0.939375000000000096 -0.047027587890625 -0.02435302734375 -0.481719970703120832 +0.9395 -0.05224609375 -0.027069091796875 -0.481719970703120832 +0.939625 -0.05224609375 -0.027069091796875 -0.481719970703120832 +0.93975 -0.05743408203125 -0.029754638671875 -0.481719970703120832 +0.939875 -0.0626220703125 -0.032440185546875 -0.481719970703120832 +0.940000000000000096 -0.0626220703125 -0.032440185546875 -0.481719970703120832 +0.940125 -0.06781005859375 -0.035125732421875 -0.481719970703120832 +0.94025 -0.06781005859375 -0.035125732421875 -0.481719970703120832 +0.940375000000000096 -0.072998046875 -0.037841796875 -0.481719970703120832 +0.9405 -0.07818603515625 -0.040496826171875 -0.481719970703120832 +0.940625 -0.07818603515625 -0.040496826171875 -0.481719970703120832 +0.94075 -0.083343505859375 -0.043182373046875 -0.481719970703120832 +0.940875 -0.083343505859375 -0.043182373046875 -0.481719970703120832 +0.941000000000000096 -0.0885009765625 -0.045867919921875 -0.481719970703120832 +0.941125 -0.093658447265625 -0.04852294921875 -0.481719970703120832 +0.94125 -0.093658447265625 -0.04852294921875 -0.481719970703120832 +0.941375000000000096 -0.098785400390625 -0.051177978515625 -0.481719970703120832 +0.9415 -0.098785400390625 -0.051177978515625 -0.481719970703120832 +0.941625 -0.103912353515625 -0.0538330078125 -0.481719970703120832 +0.94175 -0.109039306640625 -0.056488037109375 -0.481719970703120832 +0.941875 -0.109039306640625 -0.056488037109375 -0.481719970703120832 +0.942000000000000096 -0.1141357421875 -0.05914306640625 -0.481719970703120832 +0.942125 -0.1141357421875 -0.05914306640625 -0.481719970703120832 +0.94225 -0.119232177734375 -0.061798095703125 -0.481719970703120832 +0.942375000000000096 -0.124298095703125 -0.064422607421875 -0.481719970703120832 +0.9425 -0.124298095703125 -0.064422607421875 -0.481719970703120832 +0.942625 -0.129364013671875 -0.067047119140625 -0.481719970703120832 +0.94275 -0.129364013671875 -0.067047119140625 -0.481719970703120832 +0.942875 -0.134429931640625 -0.06964111328125 -0.481719970703120832 +0.943000000000000096 -0.13946533203125 -0.072265625 -0.481719970703120832 +0.943125 -0.13946533203125 -0.072265625 -0.481719970703120832 +0.94325 -0.14447021484375 -0.074859619140625 -0.481719970703120832 +0.943375000000000096 -0.14447021484375 -0.074859619140625 -0.481719970703120832 +0.9435 -0.14947509765625 -0.07745361328125 -0.481719970703120832 +0.943625 -0.15447998046875 -0.080047607421875 -0.481719970703120832 +0.94375 -0.15447998046875 -0.080047607421875 -0.481719970703120832 +0.943875 -0.159454345703125 -0.082611083984375 -0.481719970703120832 +0.944000000000000096 -0.159454345703125 -0.082611083984375 -0.481719970703120832 +0.944125 -0.164398193359375 -0.085174560546875 -0.481719970703120832 +0.94425 -0.169342041015625 -0.087738037109375 -0.481719970703120832 +0.944375 -0.169342041015625 -0.087738037109375 -0.481719970703120832 +0.9445 -0.17425537109375 -0.090301513671875 -0.481719970703120832 +0.944625 -0.17425537109375 -0.090301513671875 -0.481719970703120832 +0.94475 -0.17913818359375 -0.09283447265625 -0.481719970703120832 +0.944875 -0.18402099609375 -0.095367431640625 -0.481719970703120832 +0.945000000000000096 -0.18402099609375 -0.095367431640625 -0.481719970703120832 +0.945125 -0.188873291015625 -0.097869873046875 -0.481719970703120832 +0.94525 -0.188873291015625 -0.097869873046875 -0.481719970703120832 +0.945375 -0.1937255859375 -0.100372314453125 -0.481719970703120832 +0.9455 -0.19854736328125 -0.102874755859375 -0.481719970703120832 +0.945625 -0.19854736328125 -0.102874755859375 -0.481719970703120832 +0.94575 -0.203338623046875 -0.1053466796875 -0.481719970703120832 +0.945875 -0.203338623046875 -0.1053466796875 -0.481719970703120832 +0.946000000000000096 -0.208099365234375 -0.10784912109375 -0.481719970703120832 +0.946125 -0.212860107421875 -0.11029052734375 -0.481719970703120832 +0.94625 -0.212860107421875 -0.11029052734375 -0.481719970703120832 +0.946375 -0.21759033203125 -0.11273193359375 -0.481719970703120832 +0.9465 -0.21759033203125 -0.11273193359375 -0.481719970703120832 +0.946625 -0.2222900390625 -0.11517333984375 -0.481719970703120832 +0.94675 -0.226959228515625 -0.11761474609375 -0.481719970703120832 +0.946875 -0.226959228515625 -0.11761474609375 -0.481719970703120832 +0.947000000000000096 -0.231597900390625 -0.120025634765625 -0.481719970703120832 +0.947125 -0.231597900390625 -0.120025634765625 -0.481719970703120832 +0.94725 -0.236236572265625 -0.122406005859375 -0.481719970703120832 +0.947375 -0.2408447265625 -0.124786376953125 -0.481719970703120832 +0.9475 -0.2408447265625 -0.124786376953125 -0.481719970703120832 +0.947625 -0.24542236328125 -0.127166748046875 -0.481719970703120832 +0.94775 -0.24542236328125 -0.127166748046875 -0.481719970703120832 +0.947875 -0.249969482421875 -0.1295166015625 -0.481719970703120832 +0.948000000000000096 -0.25445556640625 -0.131866455078125 -0.481719970703120832 +0.948125 -0.25445556640625 -0.131866455078125 -0.481719970703120832 +0.94825 -0.25897216796875 -0.134185791015625 -0.481719970703120832 +0.948375 -0.25897216796875 -0.134185791015625 -0.481719970703120832 +0.9485 -0.263427734375 -0.136505126953125 -0.481719970703120832 +0.948625 -0.267852783203125 -0.1387939453125 -0.481719970703120832 +0.94875 -0.267852783203125 -0.1387939453125 -0.481719970703120832 +0.948875 -0.27227783203125 -0.141082763671875 -0.481719970703120832 +0.949000000000000096 -0.27227783203125 -0.141082763671875 -0.481719970703120832 +0.949125 -0.276641845703125 -0.143341064453125 -0.481719970703120832 +0.94925 -0.280975341796875 -0.145599365234375 -0.481719970703120832 +0.949375 -0.280975341796875 -0.145599365234375 -0.481719970703120832 +0.9495 -0.285308837890625 -0.147857666015625 -0.481719970703120832 +0.949625 -0.285308837890625 -0.147857666015625 -0.481719970703120832 +0.94975 -0.289581298828125 -0.150054931640625 -0.481719970703120832 +0.949875 -0.2938232421875 -0.152252197265625 -0.481719970703120832 +0.950000000000000096 -0.2938232421875 -0.152252197265625 -0.481719970703120832 +0.950125 -0.298065185546875 -0.154449462890625 -0.481719970703120832 +0.95025 -0.298065185546875 -0.154449462890625 -0.481719970703120832 +0.950375 -0.30224609375 -0.1566162109375 -0.481719970703120832 +0.9505 -0.306396484375 -0.158782958984375 -0.481719970703120832 +0.950625 -0.306396484375 -0.158782958984375 -0.481719970703120832 +0.95075 -0.310516357421875 -0.160919189453125 -0.481719970703120832 +0.950875 -0.310516357421875 -0.160919189453125 -0.481719970703120832 +0.951000000000000096 -0.314605712890625 -0.16302490234375 -0.481719970703120832 +0.951125 -0.31866455078125 -0.165130615234375 -0.481719970703120832 +0.95125 -0.31866455078125 -0.165130615234375 -0.481719970703120832 +0.951375 -0.322662353515625 -0.167205810546875 -0.481719970703120832 +0.9515 -0.322662353515625 -0.167205810546875 -0.481719970703120832 +0.951625 -0.32666015625 -0.169281005859375 -0.481719970703120832 +0.95175 -0.330596923828125 -0.17132568359375 -0.481719970703120832 +0.951875 -0.330596923828125 -0.17132568359375 -0.481719970703120832 +0.952000000000000096 -0.334503173828125 -0.17333984375 -0.481719970703120832 +0.952125 -0.334503173828125 -0.17333984375 -0.481719970703120832 +0.95225 -0.33837890625 -0.17535400390625 -0.481719970703120832 +0.952375 -0.34222412109375 -0.177337646484375 -0.481719970703120832 +0.9525 -0.34222412109375 -0.177337646484375 -0.481719970703120832 +0.952625 -0.34600830078125 -0.179290771484375 -0.481719970703120832 +0.95275 -0.34600830078125 -0.179290771484375 -0.481719970703120832 +0.952875 -0.349761962890625 -0.181243896484375 -0.481719970703120832 +0.953000000000000096 -0.353485107421875 -0.18316650390625 -0.481719970703120832 +0.953125 -0.353485107421875 -0.18316650390625 -0.481719970703120832 +0.95325 -0.357177734375 -0.185089111328125 -0.481719970703120832 +0.953375 -0.357177734375 -0.185089111328125 -0.481719970703120832 +0.9535 -0.360809326171875 -0.186981201171875 -0.481719970703120832 +0.953625000000000096 -0.36444091796875 -0.1888427734375 -0.481719970703120832 +0.95375 -0.36444091796875 -0.1888427734375 -0.481719970703120832 +0.953875 -0.36798095703125 -0.190704345703125 -0.481719970703120832 +0.954000000000000096 -0.36798095703125 -0.190704345703125 -0.481719970703120832 +0.954125 -0.37152099609375 -0.1925048828125 -0.481719970703120832 +0.95425 -0.375 -0.1943359375 -0.481719970703120832 +0.954375 -0.375 -0.1943359375 -0.481719970703120832 +0.9545 -0.378448486328125 -0.19610595703125 -0.481719970703120832 +0.954625000000000096 -0.378448486328125 -0.19610595703125 -0.481719970703120832 +0.95475 -0.3818359375 -0.1978759765625 -0.481719970703120832 +0.954875 -0.38519287109375 -0.199615478515625 -0.481719970703120832 +0.955000000000000096 -0.38519287109375 -0.199615478515625 -0.481719970703120832 +0.955125 -0.388519287109375 -0.201324462890625 -0.481719970703120832 +0.95525 -0.388519287109375 -0.201324462890625 -0.481719970703120832 +0.955375 -0.39178466796875 -0.203033447265625 -0.481719970703120832 +0.9555 -0.39501953125 -0.2047119140625 -0.481719970703120832 +0.955625000000000096 -0.39501953125 -0.2047119140625 -0.481719970703120832 +0.95575 -0.398193359375 -0.20635986328125 -0.481719970703120832 +0.955875 -0.398193359375 -0.20635986328125 -0.481719970703120832 +0.956000000000000096 -0.401336669921875 -0.207977294921875 -0.481719970703120832 +0.956125 -0.404449462890625 -0.2095947265625 -0.481719970703120832 +0.95625 -0.404449462890625 -0.2095947265625 -0.481719970703120832 +0.956375 -0.407501220703125 -0.211181640625 -0.481719970703120832 +0.9565 -0.407501220703125 -0.211181640625 -0.481719970703120832 +0.956625000000000096 -0.4105224609375 -0.212738037109375 -0.481719970703120832 +0.95675 -0.413482666015625 -0.214263916015625 -0.481719970703120832 +0.956875 -0.413482666015625 -0.214263916015625 -0.481719970703120832 +0.957000000000000096 -0.416412353515625 -0.215789794921875 -0.481719970703120832 +0.957125 -0.416412353515625 -0.215789794921875 -0.481719970703120832 +0.95725 -0.419281005859375 -0.217254638671875 -0.481719970703120832 +0.957375 -0.422119140625 -0.21875 -0.481719970703120832 +0.9575 -0.422119140625 -0.21875 -0.481719970703120832 +0.957625000000000096 -0.424896240234375 -0.220184326171875 -0.481719970703120832 +0.95775 -0.424896240234375 -0.220184326171875 -0.481719970703120832 +0.957875 -0.4276123046875 -0.221588134765625 -0.481719970703120832 +0.958000000000000096 -0.4302978515625 -0.222991943359375 -0.481719970703120832 +0.958125 -0.4302978515625 -0.222991943359375 -0.481719970703120832 +0.95825 -0.432952880859375 -0.224365234375 -0.481719970703120832 +0.958375 -0.432952880859375 -0.224365234375 -0.481719970703120832 +0.9585 -0.435546875 -0.2257080078125 -0.481719970703120832 +0.958625000000000096 -0.438079833984375 -0.227020263671875 -0.481719970703120832 +0.95875 -0.438079833984375 -0.227020263671875 -0.481719970703120832 +0.958875 -0.440582275390625 -0.228302001953125 -0.481719970703120832 +0.959000000000000096 -0.440582275390625 -0.228302001953125 -0.481719970703120832 +0.959125 -0.44305419921875 -0.229583740234375 -0.481719970703120832 +0.95925 -0.4454345703125 -0.2308349609375 -0.481719970703120832 +0.959375 -0.4454345703125 -0.2308349609375 -0.481719970703120832 +0.9595 -0.447784423828125 -0.2320556640625 -0.481719970703120832 +0.959625000000000096 -0.447784423828125 -0.2320556640625 -0.481719970703120832 +0.95975 -0.450103759765625 -0.233245849609375 -0.481719970703120832 +0.959875 -0.452362060546875 -0.234405517578125 -0.481719970703120832 +0.96 -0.452362060546875 -0.406951904296875 -0.1003479003906193 +0.960125 -0.454559326171875 -0.408905029296875 -0.1003479003906193 +0.96025 -0.454559326171875 -0.408905029296875 -0.1003479003906193 +0.960375 -0.45672607421875 -0.410858154296875 -0.1003479003906193 +0.9605 -0.458831787109375 -0.412750244140625 -0.1003479003906193 +0.960625000000000096 -0.458831787109375 -0.412750244140625 -0.1003479003906193 +0.96075 -0.46087646484375 -0.41461181640625 -0.1003479003906193 +0.960875 -0.46087646484375 -0.41461181640625 -0.1003479003906193 +0.961 -0.462890625 -0.416412353515625 -0.1003479003906193 +0.961125 -0.464813232421875 -0.41815185546875 -0.1003479003906193 +0.96125 -0.464813232421875 -0.41815185546875 -0.1003479003906193 +0.961375 -0.46673583984375 -0.41986083984375 -0.1003479003906193 +0.9615 -0.46673583984375 -0.41986083984375 -0.1003479003906193 +0.961625000000000096 -0.46856689453125 -0.421539306640625 -0.1003479003906193 +0.96175 -0.470367431640625 -0.42315673828125 -0.1003479003906193 +0.961875 -0.470367431640625 -0.42315673828125 -0.1003479003906193 +0.962 -0.472137451171875 -0.42474365234375 -0.1003479003906193 +0.962125 -0.472137451171875 -0.42474365234375 -0.1003479003906193 +0.96225 -0.47381591796875 -0.42626953125 -0.1003479003906193 +0.962375 -0.4754638671875 -0.427734375 -0.1003479003906193 +0.9625 -0.4754638671875 -0.427734375 -0.1003479003906193 +0.962625000000000096 -0.47705078125 -0.429168701171875 -0.1003479003906193 +0.96275 -0.47705078125 -0.429168701171875 -0.1003479003906193 +0.962875 -0.478607177734375 -0.4305419921875 -0.1003479003906193 +0.963 -0.480072021484375 -0.431884765625 -0.1003479003906193 +0.963125 -0.480072021484375 -0.431884765625 -0.1003479003906193 +0.96325 -0.48150634765625 -0.43316650390625 -0.1003479003906193 +0.963375 -0.48150634765625 -0.43316650390625 -0.1003479003906193 +0.9635 -0.48291015625 -0.434417724609375 -0.1003479003906193 +0.963625000000000096 -0.484222412109375 -0.43560791015625 -0.1003479003906193 +0.96375 -0.484222412109375 -0.43560791015625 -0.1003479003906193 +0.963875 -0.485504150390625 -0.436767578125 -0.1003479003906193 +0.964 -0.485504150390625 -0.436767578125 -0.1003479003906193 +0.964125 -0.486724853515625 -0.4378662109375 -0.1003479003906193 +0.96425 -0.487884521484375 -0.43890380859375 -0.1003479003906193 +0.964375 -0.487884521484375 -0.43890380859375 -0.1003479003906193 +0.9645 -0.489013671875 -0.439910888671875 -0.1003479003906193 +0.964625000000000096 -0.489013671875 -0.439910888671875 -0.1003479003906193 +0.96475 -0.490081787109375 -0.440887451171875 -0.1003479003906193 +0.964875 -0.4910888671875 -0.4417724609375 -0.1003479003906193 +0.965 -0.4910888671875 -0.4417724609375 -0.1003479003906193 +0.965125 -0.492034912109375 -0.442657470703125 -0.1003479003906193 +0.96525 -0.492034912109375 -0.442657470703125 -0.1003479003906193 +0.965375 -0.492950439453125 -0.443450927734375 -0.1003479003906193 +0.9655 -0.4937744140625 -0.4442138671875 -0.1003479003906193 +0.965625000000000096 -0.4937744140625 -0.4442138671875 -0.1003479003906193 +0.96575 -0.49456787109375 -0.444915771484375 -0.1003479003906193 +0.965875 -0.49456787109375 -0.444915771484375 -0.1003479003906193 +0.966 -0.49530029296875 -0.445587158203125 -0.1003479003906193 +0.966125 -0.496002197265625 -0.446197509765625 -0.1003479003906193 +0.96625 -0.496002197265625 -0.446197509765625 -0.1003479003906193 +0.966375 -0.496612548828125 -0.446746826171875 -0.1003479003906193 +0.9665 -0.496612548828125 -0.446746826171875 -0.1003479003906193 +0.966625000000000096 -0.4971923828125 -0.447296142578125 -0.1003479003906193 +0.96675 -0.497711181640625 -0.44775390625 -0.1003479003906193 +0.966875 -0.497711181640625 -0.44775390625 -0.1003479003906193 +0.967 -0.498199462890625 -0.44818115234375 -0.1003479003906193 +0.967125 -0.498199462890625 -0.44818115234375 -0.1003479003906193 +0.96725 -0.49859619140625 -0.44854736328125 -0.1003479003906193 +0.967375 -0.49896240234375 -0.4488525390625 -0.1003479003906193 +0.9675 -0.49896240234375 -0.4488525390625 -0.1003479003906193 +0.967625000000000096 -0.499267578125 -0.449127197265625 -0.1003479003906193 +0.96775 -0.499267578125 -0.449127197265625 -0.1003479003906193 +0.967875 -0.49951171875 -0.449371337890625 -0.1003479003906193 +0.968 -0.49969482421875 -0.44952392578125 -0.1003479003906193 +0.968125 -0.49969482421875 -0.44952392578125 -0.1003479003906193 +0.96825 -0.49981689453125 -0.44964599609375 -0.1003479003906193 +0.968375 -0.49981689453125 -0.44964599609375 -0.1003479003906193 +0.9685 -0.499908447265625 -0.449737548828125 -0.1003479003906193 +0.968625000000000096 -0.49993896484375 -0.44976806640625 -0.1003479003906193 +0.96875 -0.49993896484375 -0.44976806640625 -0.1003479003906193 +0.968875 -0.499908447265625 -0.449737548828125 -0.1003479003906193 +0.969 -0.499908447265625 -0.449737548828125 -0.1003479003906193 +0.969125 -0.49981689453125 -0.44964599609375 -0.1003479003906193 +0.969250000000000096 -0.49969482421875 -0.44952392578125 -0.1003479003906193 +0.969375 -0.49969482421875 -0.44952392578125 -0.1003479003906193 +0.9695 -0.49951171875 -0.449371337890625 -0.1003479003906193 +0.969625000000000096 -0.49951171875 -0.449371337890625 -0.1003479003906193 +0.96975 -0.499267578125 -0.449127197265625 -0.1003479003906193 +0.969875 -0.49896240234375 -0.4488525390625 -0.1003479003906193 +0.97 -0.49896240234375 -0.4488525390625 -0.1003479003906193 +0.970125 -0.49859619140625 -0.44854736328125 -0.1003479003906193 +0.970250000000000096 -0.49859619140625 -0.44854736328125 -0.1003479003906193 +0.970375 -0.498199462890625 -0.44818115234375 -0.1003479003906193 +0.9705 -0.497711181640625 -0.44775390625 -0.1003479003906193 +0.970625000000000096 -0.497711181640625 -0.44775390625 -0.1003479003906193 +0.97075 -0.4971923828125 -0.447296142578125 -0.1003479003906193 +0.970875 -0.4971923828125 -0.447296142578125 -0.1003479003906193 +0.971 -0.496612548828125 -0.446746826171875 -0.1003479003906193 +0.971125 -0.496002197265625 -0.446197509765625 -0.1003479003906193 +0.971250000000000096 -0.496002197265625 -0.446197509765625 -0.1003479003906193 +0.971375 -0.49530029296875 -0.445587158203125 -0.1003479003906193 +0.9715 -0.49530029296875 -0.445587158203125 -0.1003479003906193 +0.971625000000000096 -0.49456787109375 -0.444915771484375 -0.1003479003906193 +0.97175 -0.4937744140625 -0.4442138671875 -0.1003479003906193 +0.971875 -0.4937744140625 -0.4442138671875 -0.1003479003906193 +0.972 -0.492950439453125 -0.443450927734375 -0.1003479003906193 +0.972125 -0.492950439453125 -0.443450927734375 -0.1003479003906193 +0.972250000000000096 -0.492034912109375 -0.442657470703125 -0.1003479003906193 +0.972375 -0.4910888671875 -0.4417724609375 -0.1003479003906193 +0.9725 -0.4910888671875 -0.4417724609375 -0.1003479003906193 +0.972625000000000096 -0.490081787109375 -0.440887451171875 -0.1003479003906193 +0.97275 -0.490081787109375 -0.440887451171875 -0.1003479003906193 +0.972875 -0.489013671875 -0.439910888671875 -0.1003479003906193 +0.973 -0.487884521484375 -0.43890380859375 -0.1003479003906193 +0.973125 -0.487884521484375 -0.43890380859375 -0.1003479003906193 +0.973250000000000096 -0.486724853515625 -0.4378662109375 -0.1003479003906193 +0.973375 -0.486724853515625 -0.4378662109375 -0.1003479003906193 +0.9735 -0.485504150390625 -0.436767578125 -0.1003479003906193 +0.973625000000000096 -0.484222412109375 -0.43560791015625 -0.1003479003906193 +0.97375 -0.484222412109375 -0.43560791015625 -0.1003479003906193 +0.973875 -0.48291015625 -0.434417724609375 -0.1003479003906193 +0.974 -0.48291015625 -0.434417724609375 -0.1003479003906193 +0.974125 -0.48150634765625 -0.43316650390625 -0.1003479003906193 +0.974250000000000096 -0.480072021484375 -0.431884765625 -0.1003479003906193 +0.974375 -0.480072021484375 -0.431884765625 -0.1003479003906193 +0.9745 -0.478607177734375 -0.4305419921875 -0.1003479003906193 +0.974625000000000096 -0.478607177734375 -0.4305419921875 -0.1003479003906193 +0.97475 -0.47705078125 -0.429168701171875 -0.1003479003906193 +0.974875 -0.4754638671875 -0.427734375 -0.1003479003906193 +0.975 -0.4754638671875 -0.427734375 -0.1003479003906193 +0.975125 -0.47381591796875 -0.42626953125 -0.1003479003906193 +0.975250000000000096 -0.47381591796875 -0.42626953125 -0.1003479003906193 +0.975375 -0.472137451171875 -0.42474365234375 -0.1003479003906193 +0.9755 -0.470367431640625 -0.42315673828125 -0.1003479003906193 +0.975625000000000096 -0.470367431640625 -0.42315673828125 -0.1003479003906193 +0.97575 -0.46856689453125 -0.421539306640625 -0.1003479003906193 +0.975875 -0.46856689453125 -0.421539306640625 -0.1003479003906193 +0.976 -0.46673583984375 -0.41986083984375 -0.1003479003906193 +0.976125 -0.464813232421875 -0.41815185546875 -0.1003479003906193 +0.976250000000000096 -0.464813232421875 -0.41815185546875 -0.1003479003906193 +0.976375 -0.462890625 -0.416412353515625 -0.1003479003906193 +0.9765 -0.462890625 -0.416412353515625 -0.1003479003906193 +0.976625 -0.46087646484375 -0.41461181640625 -0.1003479003906193 +0.97675 -0.458831787109375 -0.412750244140625 -0.1003479003906193 +0.976875 -0.458831787109375 -0.412750244140625 -0.1003479003906193 +0.977 -0.45672607421875 -0.410858154296875 -0.1003479003906193 +0.977125 -0.45672607421875 -0.410858154296875 -0.1003479003906193 +0.977250000000000096 -0.454559326171875 -0.408905029296875 -0.1003479003906193 +0.977375 -0.452362060546875 -0.406951904296875 -0.1003479003906193 +0.9775 -0.452362060546875 -0.406951904296875 -0.1003479003906193 +0.977625 -0.450103759765625 -0.4049072265625 -0.1003479003906193 +0.97775 -0.450103759765625 -0.4049072265625 -0.1003479003906193 +0.977875 -0.447784423828125 -0.40283203125 -0.1003479003906193 +0.978 -0.4454345703125 -0.400726318359375 -0.1003479003906193 +0.978125 -0.4454345703125 -0.400726318359375 -0.1003479003906193 +0.978250000000000096 -0.44305419921875 -0.3985595703125 -0.1003479003906193 +0.978375 -0.44305419921875 -0.3985595703125 -0.1003479003906193 +0.9785 -0.440582275390625 -0.3963623046875 -0.1003479003906193 +0.978625 -0.438079833984375 -0.39410400390625 -0.1003479003906193 +0.97875 -0.438079833984375 -0.39410400390625 -0.1003479003906193 +0.978875 -0.435546875 -0.391815185546875 -0.1003479003906193 +0.979 -0.435546875 -0.391815185546875 -0.1003479003906193 +0.979125 -0.432952880859375 -0.389495849609375 -0.1003479003906193 +0.979250000000000096 -0.4302978515625 -0.387115478515625 -0.1003479003906193 +0.979375 -0.4302978515625 -0.387115478515625 -0.1003479003906193 +0.9795 -0.4276123046875 -0.38470458984375 -0.1003479003906193 +0.979625 -0.4276123046875 -0.38470458984375 -0.1003479003906193 +0.97975 -0.424896240234375 -0.382232666015625 -0.1003479003906193 +0.979875 -0.422119140625 -0.379730224609375 -0.1003479003906193 +0.98 -0.422119140625 -0.379730224609375 -0.1003479003906193 +0.980125 -0.419281005859375 -0.377197265625 -0.1003479003906193 +0.980250000000000096 -0.419281005859375 -0.377197265625 -0.1003479003906193 +0.980375 -0.416412353515625 -0.374603271484375 -0.1003479003906193 +0.9805 -0.413482666015625 -0.371978759765625 -0.1003479003906193 +0.980625 -0.413482666015625 -0.371978759765625 -0.1003479003906193 +0.98075 -0.4105224609375 -0.369293212890625 -0.1003479003906193 +0.980875 -0.4105224609375 -0.369293212890625 -0.1003479003906193 +0.981 -0.407501220703125 -0.366607666015625 -0.1003479003906193 +0.981125 -0.404449462890625 -0.363861083984375 -0.1003479003906193 +0.981250000000000096 -0.404449462890625 -0.363861083984375 -0.1003479003906193 +0.981375 -0.401336669921875 -0.361053466796875 -0.1003479003906193 +0.9815 -0.401336669921875 -0.361053466796875 -0.1003479003906193 +0.981625 -0.398193359375 -0.35821533203125 -0.1003479003906193 +0.98175 -0.39501953125 -0.355377197265625 -0.1003479003906193 +0.981875 -0.39501953125 -0.355377197265625 -0.1003479003906193 +0.982 -0.39178466796875 -0.352447509765625 -0.1003479003906193 +0.982125 -0.39178466796875 -0.352447509765625 -0.1003479003906193 +0.982250000000000096 -0.388519287109375 -0.349517822265625 -0.1003479003906193 +0.982375 -0.38519287109375 -0.346527099609375 -0.1003479003906193 +0.9825 -0.38519287109375 -0.346527099609375 -0.1003479003906193 +0.982625 -0.3818359375 -0.343505859375 -0.1003479003906193 +0.98275 -0.3818359375 -0.343505859375 -0.1003479003906193 +0.982875 -0.378448486328125 -0.3404541015625 -0.1003479003906193 +0.983 -0.375 -0.33734130859375 -0.1003479003906193 +0.983125 -0.375 -0.33734130859375 -0.1003479003906193 +0.983250000000000096 -0.37152099609375 -0.334228515625 -0.1003479003906193 +0.983375 -0.37152099609375 -0.334228515625 -0.1003479003906193 +0.9835 -0.36798095703125 -0.3310546875 -0.1003479003906193 +0.983625 -0.36444091796875 -0.327850341796875 -0.1003479003906193 +0.98375 -0.36444091796875 -0.327850341796875 -0.1003479003906193 +0.983875 -0.360809326171875 -0.3245849609375 -0.1003479003906193 +0.984 -0.360809326171875 -0.3245849609375 -0.1003479003906193 +0.984125 -0.357177734375 -0.321319580078125 -0.1003479003906193 +0.984250000000000096 -0.353485107421875 -0.318023681640625 -0.1003479003906193 +0.984375 -0.353485107421875 -0.318023681640625 -0.1003479003906193 +0.9845 -0.349761962890625 -0.314666748046875 -0.1003479003906193 +0.984625 -0.349761962890625 -0.314666748046875 -0.1003479003906193 +0.98475 -0.34600830078125 -0.311279296875 -0.1003479003906193 +0.984875000000000096 -0.34222412109375 -0.307861328125 -0.1003479003906193 +0.985 -0.34222412109375 -0.307861328125 -0.1003479003906193 +0.985125 -0.33837890625 -0.304412841796875 -0.1003479003906193 +0.985250000000000096 -0.33837890625 -0.304412841796875 -0.1003479003906193 +0.985375 -0.334503173828125 -0.300933837890625 -0.1003479003906193 +0.9855 -0.330596923828125 -0.29742431640625 -0.1003479003906193 +0.985625 -0.330596923828125 -0.29742431640625 -0.1003479003906193 +0.98575 -0.32666015625 -0.293853759765625 -0.1003479003906193 +0.985875000000000096 -0.32666015625 -0.293853759765625 -0.1003479003906193 +0.986 -0.322662353515625 -0.290283203125 -0.1003479003906193 +0.986125 -0.31866455078125 -0.28668212890625 -0.1003479003906193 +0.986250000000000096 -0.31866455078125 -0.28668212890625 -0.1003479003906193 +0.986375 -0.314605712890625 -0.28302001953125 -0.1003479003906193 +0.9865 -0.314605712890625 -0.28302001953125 -0.1003479003906193 +0.986625 -0.310516357421875 -0.27935791015625 -0.1003479003906193 +0.98675 -0.306396484375 -0.275634765625 -0.1003479003906193 +0.986875000000000096 -0.306396484375 -0.275634765625 -0.1003479003906193 +0.987 -0.30224609375 -0.27191162109375 -0.1003479003906193 +0.987125 -0.30224609375 -0.27191162109375 -0.1003479003906193 +0.987250000000000096 -0.298065185546875 -0.26812744140625 -0.1003479003906193 +0.987375 -0.2938232421875 -0.26434326171875 -0.1003479003906193 +0.9875 -0.2938232421875 -0.26434326171875 -0.1003479003906193 +0.987625 -0.289581298828125 -0.260528564453125 -0.1003479003906193 +0.98775 -0.289581298828125 -0.260528564453125 -0.1003479003906193 +0.987875000000000096 -0.285308837890625 -0.25665283203125 -0.1003479003906193 +0.988 -0.280975341796875 -0.252777099609375 -0.1003479003906193 +0.988125 -0.280975341796875 -0.252777099609375 -0.1003479003906193 +0.988250000000000096 -0.276641845703125 -0.248870849609375 -0.1003479003906193 +0.988375 -0.276641845703125 -0.248870849609375 -0.1003479003906193 +0.9885 -0.27227783203125 -0.24493408203125 -0.1003479003906193 +0.988625 -0.267852783203125 -0.240966796875 -0.1003479003906193 +0.98875 -0.267852783203125 -0.240966796875 -0.1003479003906193 +0.988875000000000096 -0.263427734375 -0.236968994140625 -0.1003479003906193 +0.989 -0.263427734375 -0.236968994140625 -0.1003479003906193 +0.989125 -0.25897216796875 -0.23297119140625 -0.1003479003906193 +0.989250000000000096 -0.25445556640625 -0.228912353515625 -0.1003479003906193 +0.989375 -0.25445556640625 -0.228912353515625 -0.1003479003906193 +0.9895 -0.249969482421875 -0.224853515625 -0.1003479003906193 +0.989625 -0.249969482421875 -0.224853515625 -0.1003479003906193 +0.98975 -0.24542236328125 -0.22076416015625 -0.1003479003906193 +0.989875000000000096 -0.2408447265625 -0.216644287109375 -0.1003479003906193 +0.99 -0.2408447265625 -0.216644287109375 -0.1003479003906193 +0.990125 -0.236236572265625 -0.2125244140625 -0.1003479003906193 +0.990250000000000096 -0.236236572265625 -0.2125244140625 -0.1003479003906193 +0.990375 -0.231597900390625 -0.208343505859375 -0.1003479003906193 +0.9905 -0.226959228515625 -0.20416259765625 -0.1003479003906193 +0.990625 -0.226959228515625 -0.20416259765625 -0.1003479003906193 +0.99075 -0.2222900390625 -0.199951171875 -0.1003479003906193 +0.990875000000000096 -0.2222900390625 -0.199951171875 -0.1003479003906193 +0.991 -0.21759033203125 -0.19573974609375 -0.1003479003906193 +0.991125 -0.212860107421875 -0.19146728515625 -0.1003479003906193 +0.991250000000000096 -0.212860107421875 -0.19146728515625 -0.1003479003906193 +0.991375 -0.208099365234375 -0.187225341796875 -0.1003479003906193 +0.9915 -0.208099365234375 -0.187225341796875 -0.1003479003906193 +0.991625 -0.203338623046875 -0.18292236328125 -0.1003479003906193 +0.99175 -0.19854736328125 -0.178619384765625 -0.1003479003906193 +0.991875000000000096 -0.19854736328125 -0.178619384765625 -0.1003479003906193 +0.992 -0.13616943359375 -0.1937255859375 0.2970092773437556 +0.992125 -0.13616943359375 -0.1937255859375 0.2970092773437556 +0.99225 -0.132781982421875 -0.188873291015625 0.2970092773437556 +0.992375 -0.129364013671875 -0.18402099609375 0.2970092773437556 +0.9925 -0.129364013671875 -0.18402099609375 0.2970092773437556 +0.992625 -0.12591552734375 -0.17913818359375 0.2970092773437556 +0.99275 -0.12591552734375 -0.17913818359375 0.2970092773437556 +0.992875000000000096 -0.122467041015625 -0.17425537109375 0.2970092773437556 +0.993 -0.1190185546875 -0.169342041015625 0.2970092773437556 +0.993125 -0.1190185546875 -0.169342041015625 0.2970092773437556 +0.99325 -0.11553955078125 -0.164398193359375 0.2970092773437556 +0.993375 -0.11553955078125 -0.164398193359375 0.2970092773437556 +0.9935 -0.112091064453125 -0.159454345703125 0.2970092773437556 +0.993625 -0.10858154296875 -0.15447998046875 0.2970092773437556 +0.99375 -0.10858154296875 -0.15447998046875 0.2970092773437556 +0.993875000000000096 -0.105072021484375 -0.14947509765625 0.2970092773437556 +0.994 -0.105072021484375 -0.14947509765625 0.2970092773437556 +0.994125 -0.1015625 -0.14447021484375 0.2970092773437556 +0.99425 -0.0980224609375 -0.13946533203125 0.2970092773437556 +0.994375 -0.0980224609375 -0.13946533203125 0.2970092773437556 +0.9945 -0.094482421875 -0.134429931640625 0.2970092773437556 +0.994625 -0.094482421875 -0.134429931640625 0.2970092773437556 +0.99475 -0.0909423828125 -0.129364013671875 0.2970092773437556 +0.994875000000000096 -0.087371826171875 -0.124298095703125 0.2970092773437556 +0.995 -0.087371826171875 -0.124298095703125 0.2970092773437556 +0.995125 -0.08380126953125 -0.119232177734375 0.2970092773437556 +0.99525 -0.08380126953125 -0.119232177734375 0.2970092773437556 +0.995375 -0.080230712890625 -0.1141357421875 0.2970092773437556 +0.9955 -0.076629638671875 -0.109039306640625 0.2970092773437556 +0.995625 -0.076629638671875 -0.109039306640625 0.2970092773437556 +0.99575 -0.07305908203125 -0.103912353515625 0.2970092773437556 +0.995875000000000096 -0.07305908203125 -0.103912353515625 0.2970092773437556 +0.996 -0.0694580078125 -0.098785400390625 0.2970092773437556 +0.996125 -0.065826416015625 -0.093658447265625 0.2970092773437556 +0.99625 -0.065826416015625 -0.093658447265625 0.2970092773437556 +0.996375 -0.062225341796875 -0.0885009765625 0.2970092773437556 +0.9965 -0.062225341796875 -0.0885009765625 0.2970092773437556 +0.996625 -0.05859375 -0.083343505859375 0.2970092773437556 +0.99675 -0.054962158203125 -0.07818603515625 0.2970092773437556 +0.996875000000000096 -0.054962158203125 -0.07818603515625 0.2970092773437556 +0.997 -0.05133056640625 -0.072998046875 0.2970092773437556 +0.997125 -0.05133056640625 -0.072998046875 0.2970092773437556 +0.99725 -0.04766845703125 -0.06781005859375 0.2970092773437556 +0.997375 -0.044036865234375 -0.0626220703125 0.2970092773437556 +0.9975 -0.044036865234375 -0.0626220703125 0.2970092773437556 +0.997625 -0.040374755859375 -0.05743408203125 0.2970092773437556 +0.99775 -0.040374755859375 -0.05743408203125 0.2970092773437556 +0.997875000000000096 -0.036712646484375 -0.05224609375 0.2970092773437556 +0.998 -0.033050537109375 -0.047027587890625 0.2970092773437556 +0.998125 -0.033050537109375 -0.047027587890625 0.2970092773437556 +0.99825 -0.029388427734375 -0.04180908203125 0.2970092773437556 +0.998375 -0.029388427734375 -0.04180908203125 0.2970092773437556 +0.9985 -0.025726318359375 -0.036590576171875 0.2970092773437556 +0.998625 -0.02203369140625 -0.0313720703125 0.2970092773437556 +0.99875 -0.02203369140625 -0.0313720703125 0.2970092773437556 +0.998875000000000096 -0.01837158203125 -0.026123046875 0.2970092773437556 +0.999 -0.01837158203125 -0.026123046875 0.2970092773437556 +0.999125 -0.01470947265625 -0.020904541015625 0.2970092773437556 +0.99925 -0.011016845703125 -0.01568603515625 0.2970092773437556 +0.999375 -0.011016845703125 -0.01568603515625 0.2970092773437556 +0.9995 -0.007354736328125 -0.01043701171875 0.2970092773437556 +0.999625 -0.007354736328125 -0.01043701171875 0.2970092773437556 +0.99975 -0.003662109375 -0.005218505859375 0.2970092773437556 +0.999875000000000096 0.0 0.0 0.2970092773437556 +1.0 0.0 0.0 0.2970092773437556 +1.000125 0.003662109375 0.005218505859375 0.2970092773437556 +1.00025 0.003662109375 0.005218505859375 0.2970092773437556 +1.000375 0.007354736328125 0.01043701171875 0.2970092773437556 +1.0005 0.011016845703125 0.01568603515625 0.2970092773437556 +1.000625 0.011016845703125 0.01568603515625 0.2970092773437556 +1.00075 0.01470947265625 0.020904541015625 0.2970092773437556 +1.000875 0.01470947265625 0.020904541015625 0.2970092773437556 +1.0010000000000002 0.01837158203125 0.026123046875 0.2970092773437556 +1.001125 0.02203369140625 0.0313720703125 0.2970092773437556 +1.00125 0.02203369140625 0.0313720703125 0.2970092773437556 +1.0013750000000002 0.025726318359375 0.036590576171875 0.2970092773437556 +1.0015 0.025726318359375 0.036590576171875 0.2970092773437556 +1.001625 0.029388427734375 0.04180908203125 0.2970092773437556 +1.00175 0.033050537109375 0.047027587890625 0.2970092773437556 +1.001875 0.033050537109375 0.047027587890625 0.2970092773437556 +1.002 0.036712646484375 0.05224609375 0.2970092773437556 +1.002125 0.036712646484375 0.05224609375 0.2970092773437556 +1.00225 0.040374755859375 0.05743408203125 0.2970092773437556 +1.002375 0.044036865234375 0.0626220703125 0.2970092773437556 +1.0025 0.044036865234375 0.0626220703125 0.2970092773437556 +1.002625 0.04766845703125 0.06781005859375 0.2970092773437556 +1.00275 0.04766845703125 0.06781005859375 0.2970092773437556 +1.002875 0.05133056640625 0.072998046875 0.2970092773437556 +1.0030000000000002 0.054962158203125 0.07818603515625 0.2970092773437556 +1.003125 0.054962158203125 0.07818603515625 0.2970092773437556 +1.00325 0.05859375 0.083343505859375 0.2970092773437556 +1.0033750000000002 0.05859375 0.083343505859375 0.2970092773437556 +1.0035 0.062225341796875 0.0885009765625 0.2970092773437556 +1.003625 0.065826416015625 0.093658447265625 0.2970092773437556 +1.00375 0.065826416015625 0.093658447265625 0.2970092773437556 +1.003875 0.0694580078125 0.098785400390625 0.2970092773437556 +1.004 0.0694580078125 0.098785400390625 0.2970092773437556 +1.004125 0.07305908203125 0.103912353515625 0.2970092773437556 +1.00425 0.076629638671875 0.109039306640625 0.2970092773437556 +1.004375 0.076629638671875 0.109039306640625 0.2970092773437556 +1.0045 0.080230712890625 0.1141357421875 0.2970092773437556 +1.004625 0.080230712890625 0.1141357421875 0.2970092773437556 +1.00475 0.08380126953125 0.119232177734375 0.2970092773437556 +1.004875 0.087371826171875 0.124298095703125 0.2970092773437556 +1.0050000000000002 0.087371826171875 0.124298095703125 0.2970092773437556 +1.005125 0.0909423828125 0.129364013671875 0.2970092773437556 +1.00525 0.0909423828125 0.129364013671875 0.2970092773437556 +1.0053750000000002 0.094482421875 0.134429931640625 0.2970092773437556 +1.0055 0.0980224609375 0.13946533203125 0.2970092773437556 +1.005625 0.0980224609375 0.13946533203125 0.2970092773437556 +1.00575 0.1015625 0.14447021484375 0.2970092773437556 +1.005875 0.1015625 0.14447021484375 0.2970092773437556 +1.006 0.105072021484375 0.14947509765625 0.2970092773437556 +1.006125 0.10858154296875 0.15447998046875 0.2970092773437556 +1.00625 0.10858154296875 0.15447998046875 0.2970092773437556 +1.006375 0.112091064453125 0.159454345703125 0.2970092773437556 +1.0065 0.112091064453125 0.159454345703125 0.2970092773437556 +1.006625 0.11553955078125 0.164398193359375 0.2970092773437556 +1.00675 0.1190185546875 0.169342041015625 0.2970092773437556 +1.006875 0.1190185546875 0.169342041015625 0.2970092773437556 +1.0070000000000002 0.122467041015625 0.17425537109375 0.2970092773437556 +1.007125 0.122467041015625 0.17425537109375 0.2970092773437556 +1.00725 0.12591552734375 0.17913818359375 0.2970092773437556 +1.0073750000000002 0.129364013671875 0.18402099609375 0.2970092773437556 +1.0075 0.129364013671875 0.18402099609375 0.2970092773437556 +1.007625 0.132781982421875 0.188873291015625 0.2970092773437556 +1.00775 0.132781982421875 0.188873291015625 0.2970092773437556 +1.007875 0.13616943359375 0.1937255859375 0.2970092773437556 +1.008 0.139556884765625 0.19854736328125 0.2970092773437556 +1.008125 0.139556884765625 0.19854736328125 0.2970092773437556 +1.00825 0.142913818359375 0.203338623046875 0.2970092773437556 +1.008375 0.142913818359375 0.203338623046875 0.2970092773437556 +1.0085 0.146270751953125 0.208099365234375 0.2970092773437556 +1.008625 0.149627685546875 0.212860107421875 0.2970092773437556 +1.00875 0.149627685546875 0.212860107421875 0.2970092773437556 +1.008875 0.1529541015625 0.21759033203125 0.2970092773437556 +1.0090000000000002 0.1529541015625 0.21759033203125 0.2970092773437556 +1.009125 0.15625 0.2222900390625 0.2970092773437556 +1.00925 0.159515380859375 0.226959228515625 0.2970092773437556 +1.009375 0.159515380859375 0.226959228515625 0.2970092773437556 +1.0095 0.162811279296875 0.231597900390625 0.2970092773437556 +1.009625 0.162811279296875 0.231597900390625 0.2970092773437556 +1.00975 0.166046142578125 0.236236572265625 0.2970092773437556 +1.009875 0.169281005859375 0.2408447265625 0.2970092773437556 +1.01 0.169281005859375 0.2408447265625 0.2970092773437556 +1.010125 0.172515869140625 0.24542236328125 0.2970092773437556 +1.01025 0.172515869140625 0.24542236328125 0.2970092773437556 +1.010375 0.175689697265625 0.249969482421875 0.2970092773437556 +1.0105 0.178863525390625 0.25445556640625 0.2970092773437556 +1.010625 0.178863525390625 0.25445556640625 0.2970092773437556 +1.01075 0.182037353515625 0.25897216796875 0.2970092773437556 +1.010875 0.182037353515625 0.25897216796875 0.2970092773437556 +1.0110000000000002 0.1851806640625 0.263427734375 0.2970092773437556 +1.011125 0.18829345703125 0.267852783203125 0.2970092773437556 +1.01125 0.18829345703125 0.267852783203125 0.2970092773437556 +1.011375 0.19140625 0.27227783203125 0.2970092773437556 +1.0115 0.19140625 0.27227783203125 0.2970092773437556 +1.011625 0.1944580078125 0.276641845703125 0.2970092773437556 +1.01175 0.197509765625 0.280975341796875 0.2970092773437556 +1.011875 0.197509765625 0.280975341796875 0.2970092773437556 +1.012 0.2005615234375 0.285308837890625 0.2970092773437556 +1.012125 0.2005615234375 0.285308837890625 0.2970092773437556 +1.01225 0.20355224609375 0.289581298828125 0.2970092773437556 +1.012375 0.20654296875 0.2938232421875 0.2970092773437556 +1.0125 0.20654296875 0.2938232421875 0.2970092773437556 +1.012625 0.20953369140625 0.298065185546875 0.2970092773437556 +1.01275 0.20953369140625 0.298065185546875 0.2970092773437556 +1.012875 0.21246337890625 0.30224609375 0.2970092773437556 +1.0130000000000002 0.21539306640625 0.306396484375 0.2970092773437556 +1.013125 0.21539306640625 0.306396484375 0.2970092773437556 +1.01325 0.218292236328125 0.310516357421875 0.2970092773437556 +1.013375 0.218292236328125 0.310516357421875 0.2970092773437556 +1.0135 0.221160888671875 0.314605712890625 0.2970092773437556 +1.013625 0.2239990234375 0.31866455078125 0.2970092773437556 +1.01375 0.2239990234375 0.31866455078125 0.2970092773437556 +1.013875 0.226806640625 0.322662353515625 0.2970092773437556 +1.014 0.226806640625 0.322662353515625 0.2970092773437556 +1.014125 0.2296142578125 0.32666015625 0.2970092773437556 +1.01425 0.232391357421875 0.330596923828125 0.2970092773437556 +1.014375 0.232391357421875 0.330596923828125 0.2970092773437556 +1.0145 0.235137939453125 0.334503173828125 0.2970092773437556 +1.014625 0.235137939453125 0.334503173828125 0.2970092773437556 +1.01475 0.23785400390625 0.33837890625 0.2970092773437556 +1.014875 0.240570068359375 0.34222412109375 0.2970092773437556 +1.0150000000000002 0.240570068359375 0.34222412109375 0.2970092773437556 +1.015125 0.24322509765625 0.34600830078125 0.2970092773437556 +1.01525 0.24322509765625 0.34600830078125 0.2970092773437556 +1.015375 0.245880126953125 0.349761962890625 0.2970092773437556 +1.0155 0.24847412109375 0.353485107421875 0.2970092773437556 +1.015625 0.24847412109375 0.353485107421875 0.2970092773437556 +1.01575 0.251068115234375 0.357177734375 0.2970092773437556 +1.015875 0.251068115234375 0.357177734375 0.2970092773437556 +1.016 0.253631591796875 0.360809326171875 0.2970092773437556 +1.016125 0.25616455078125 0.36444091796875 0.2970092773437556 +1.01625 0.25616455078125 0.36444091796875 0.2970092773437556 +1.016375 0.2586669921875 0.36798095703125 0.2970092773437556 +1.0165 0.2586669921875 0.36798095703125 0.2970092773437556 +1.0166250000000002 0.261138916015625 0.37152099609375 0.2970092773437556 +1.01675 0.26361083984375 0.375 0.2970092773437556 +1.016875 0.26361083984375 0.375 0.2970092773437556 +1.0170000000000002 0.266021728515625 0.378448486328125 0.2970092773437556 +1.017125 0.266021728515625 0.378448486328125 0.2970092773437556 +1.01725 0.268402099609375 0.3818359375 0.2970092773437556 +1.017375 0.270782470703125 0.38519287109375 0.2970092773437556 +1.0175 0.270782470703125 0.38519287109375 0.2970092773437556 +1.017625 0.273101806640625 0.388519287109375 0.2970092773437556 +1.01775 0.273101806640625 0.388519287109375 0.2970092773437556 +1.017875 0.275421142578125 0.39178466796875 0.2970092773437556 +1.018 0.277679443359375 0.39501953125 0.2970092773437556 +1.018125 0.277679443359375 0.39501953125 0.2970092773437556 +1.01825 0.2799072265625 0.398193359375 0.2970092773437556 +1.018375 0.2799072265625 0.398193359375 0.2970092773437556 +1.0185 0.282135009765625 0.401336669921875 0.2970092773437556 +1.0186250000000002 0.2843017578125 0.404449462890625 0.2970092773437556 +1.01875 0.2843017578125 0.404449462890625 0.2970092773437556 +1.018875 0.286468505859375 0.407501220703125 0.2970092773437556 +1.0190000000000002 0.286468505859375 0.407501220703125 0.2970092773437556 +1.019125 0.28857421875 0.4105224609375 0.2970092773437556 +1.01925 0.2906494140625 0.413482666015625 0.2970092773437556 +1.019375 0.2906494140625 0.413482666015625 0.2970092773437556 +1.0195 0.292694091796875 0.416412353515625 0.2970092773437556 +1.019625 0.292694091796875 0.416412353515625 0.2970092773437556 +1.01975 0.29473876953125 0.419281005859375 0.2970092773437556 +1.019875 0.296722412109375 0.422119140625 0.2970092773437556 +1.02 0.296722412109375 0.422119140625 0.2970092773437556 +1.020125 0.298675537109375 0.424896240234375 0.2970092773437556 +1.02025 0.298675537109375 0.424896240234375 0.2970092773437556 +1.020375 0.30059814453125 0.4276123046875 0.2970092773437556 +1.0205 0.302490234375 0.4302978515625 0.2970092773437556 +1.0206250000000002 0.302490234375 0.4302978515625 0.2970092773437556 +1.02075 0.304351806640625 0.432952880859375 0.2970092773437556 +1.020875 0.304351806640625 0.432952880859375 0.2970092773437556 +1.0210000000000002 0.306182861328125 0.435546875 0.2970092773437556 +1.021125 0.307952880859375 0.438079833984375 0.2970092773437556 +1.02125 0.307952880859375 0.438079833984375 0.2970092773437556 +1.021375 0.3096923828125 0.440582275390625 0.2970092773437556 +1.0215 0.3096923828125 0.440582275390625 0.2970092773437556 +1.021625 0.311431884765625 0.44305419921875 0.2970092773437556 +1.02175 0.3131103515625 0.4454345703125 0.2970092773437556 +1.021875 0.3131103515625 0.4454345703125 0.2970092773437556 +1.022 0.31475830078125 0.447784423828125 0.2970092773437556 +1.022125 0.31475830078125 0.447784423828125 0.2970092773437556 +1.02225 0.316375732421875 0.450103759765625 0.2970092773437556 +1.022375 0.317962646484375 0.452362060546875 0.2970092773437556 +1.0225 0.317962646484375 0.452362060546875 0.2970092773437556 +1.0226250000000002 0.31951904296875 0.454559326171875 0.2970092773437556 +1.02275 0.31951904296875 0.454559326171875 0.2970092773437556 +1.022875 0.321044921875 0.45672607421875 0.2970092773437556 +1.0230000000000002 0.322509765625 0.458831787109375 0.2970092773437556 +1.023125 0.322509765625 0.458831787109375 0.2970092773437556 +1.02325 0.323974609375 0.46087646484375 0.2970092773437556 +1.023375 0.323974609375 0.46087646484375 0.2970092773437556 +1.0235 0.32537841796875 0.462890625 0.2970092773437556 +1.023625 0.326751708984375 0.464813232421875 0.2970092773437556 +1.02375 0.326751708984375 0.464813232421875 0.2970092773437556 +1.023875 0.328094482421875 0.46673583984375 0.2970092773437556 +1.024 0.164703369140625 0.46673583984375 0.6470092773437545 +1.024125 0.165374755859375 0.46856689453125 0.6470092773437545 +1.02425 0.165985107421875 0.470367431640625 0.6470092773437545 +1.024375 0.165985107421875 0.470367431640625 0.6470092773437545 +1.0245 0.1666259765625 0.472137451171875 0.6470092773437545 +1.0246250000000002 0.1666259765625 0.472137451171875 0.6470092773437545 +1.02475 0.167205810546875 0.47381591796875 0.6470092773437545 +1.024875 0.16778564453125 0.4754638671875 0.6470092773437545 +1.025 0.16778564453125 0.4754638671875 0.6470092773437545 +1.025125 0.168365478515625 0.47705078125 0.6470092773437545 +1.02525 0.168365478515625 0.47705078125 0.6470092773437545 +1.025375 0.16888427734375 0.478607177734375 0.6470092773437545 +1.0255 0.16943359375 0.480072021484375 0.6470092773437545 +1.025625 0.16943359375 0.480072021484375 0.6470092773437545 +1.02575 0.169921875 0.48150634765625 0.6470092773437545 +1.025875 0.169921875 0.48150634765625 0.6470092773437545 +1.026 0.17041015625 0.48291015625 0.6470092773437545 +1.026125 0.1708984375 0.484222412109375 0.6470092773437545 +1.02625 0.1708984375 0.484222412109375 0.6470092773437545 +1.026375 0.17132568359375 0.485504150390625 0.6470092773437545 +1.0265 0.17132568359375 0.485504150390625 0.6470092773437545 +1.0266250000000002 0.171783447265625 0.486724853515625 0.6470092773437545 +1.02675 0.17218017578125 0.487884521484375 0.6470092773437545 +1.026875 0.17218017578125 0.487884521484375 0.6470092773437545 +1.027 0.172576904296875 0.489013671875 0.6470092773437545 +1.027125 0.172576904296875 0.489013671875 0.6470092773437545 +1.02725 0.172943115234375 0.490081787109375 0.6470092773437545 +1.027375 0.173309326171875 0.4910888671875 0.6470092773437545 +1.0275 0.173309326171875 0.4910888671875 0.6470092773437545 +1.027625 0.17364501953125 0.492034912109375 0.6470092773437545 +1.02775 0.17364501953125 0.492034912109375 0.6470092773437545 +1.027875 0.1739501953125 0.492950439453125 0.6470092773437545 +1.028 0.17425537109375 0.4937744140625 0.6470092773437545 +1.028125 0.17425537109375 0.4937744140625 0.6470092773437545 +1.02825 0.174530029296875 0.49456787109375 0.6470092773437545 +1.028375 0.174530029296875 0.49456787109375 0.6470092773437545 +1.0285 0.1748046875 0.49530029296875 0.6470092773437545 +1.0286250000000002 0.175048828125 0.496002197265625 0.6470092773437545 +1.02875 0.175048828125 0.496002197265625 0.6470092773437545 +1.028875 0.175262451171875 0.496612548828125 0.6470092773437545 +1.029 0.175262451171875 0.496612548828125 0.6470092773437545 +1.029125 0.17547607421875 0.4971923828125 0.6470092773437545 +1.02925 0.1756591796875 0.497711181640625 0.6470092773437545 +1.029375 0.1756591796875 0.497711181640625 0.6470092773437545 +1.0295 0.175811767578125 0.498199462890625 0.6470092773437545 +1.029625 0.175811767578125 0.498199462890625 0.6470092773437545 +1.02975 0.17596435546875 0.49859619140625 0.6470092773437545 +1.029875 0.17608642578125 0.49896240234375 0.6470092773437545 +1.03 0.17608642578125 0.49896240234375 0.6470092773437545 +1.030125 0.176177978515625 0.499267578125 0.6470092773437545 +1.03025 0.176177978515625 0.499267578125 0.6470092773437545 +1.030375 0.17626953125 0.49951171875 0.6470092773437545 +1.0305 0.17633056640625 0.49969482421875 0.6470092773437545 +1.0306250000000002 0.17633056640625 0.49969482421875 0.6470092773437545 +1.03075 0.1763916015625 0.49981689453125 0.6470092773437545 +1.030875 0.1763916015625 0.49981689453125 0.6470092773437545 +1.031 0.176422119140625 0.499908447265625 0.6470092773437545 +1.031125 0.176422119140625 0.49993896484375 0.6470092773437545 +1.03125 0.176422119140625 0.49993896484375 0.6470092773437545 +1.031375 0.176422119140625 0.499908447265625 0.6470092773437545 +1.0315 0.176422119140625 0.499908447265625 0.6470092773437545 +1.031625 0.1763916015625 0.49981689453125 0.6470092773437545 +1.03175 0.17633056640625 0.49969482421875 0.6470092773437545 +1.031875 0.17633056640625 0.49969482421875 0.6470092773437545 +1.032 0.17626953125 0.49951171875 0.6470092773437545 +1.032125 0.17626953125 0.49951171875 0.6470092773437545 +1.0322500000000002 0.176177978515625 0.499267578125 0.6470092773437545 +1.032375 0.17608642578125 0.49896240234375 0.6470092773437545 +1.0325 0.17608642578125 0.49896240234375 0.6470092773437545 +1.0326250000000002 0.17596435546875 0.49859619140625 0.6470092773437545 +1.03275 0.17596435546875 0.49859619140625 0.6470092773437545 +1.032875 0.175811767578125 0.498199462890625 0.6470092773437545 +1.033 0.1756591796875 0.497711181640625 0.6470092773437545 +1.033125 0.1756591796875 0.497711181640625 0.6470092773437545 +1.03325 0.17547607421875 0.4971923828125 0.6470092773437545 +1.033375 0.17547607421875 0.4971923828125 0.6470092773437545 +1.0335 0.175262451171875 0.496612548828125 0.6470092773437545 +1.033625 0.175048828125 0.496002197265625 0.6470092773437545 +1.03375 0.175048828125 0.496002197265625 0.6470092773437545 +1.033875 0.1748046875 0.49530029296875 0.6470092773437545 +1.034 0.1748046875 0.49530029296875 0.6470092773437545 +1.034125 0.174530029296875 0.49456787109375 0.6470092773437545 +1.0342500000000002 0.17425537109375 0.4937744140625 0.6470092773437545 +1.034375 0.17425537109375 0.4937744140625 0.6470092773437545 +1.0345 0.1739501953125 0.492950439453125 0.6470092773437545 +1.0346250000000002 0.1739501953125 0.492950439453125 0.6470092773437545 +1.03475 0.17364501953125 0.492034912109375 0.6470092773437545 +1.034875 0.173309326171875 0.4910888671875 0.6470092773437545 +1.035 0.173309326171875 0.4910888671875 0.6470092773437545 +1.035125 0.172943115234375 0.490081787109375 0.6470092773437545 +1.03525 0.172943115234375 0.490081787109375 0.6470092773437545 +1.035375 0.172576904296875 0.489013671875 0.6470092773437545 +1.0355 0.17218017578125 0.487884521484375 0.6470092773437545 +1.035625 0.17218017578125 0.487884521484375 0.6470092773437545 +1.03575 0.171783447265625 0.486724853515625 0.6470092773437545 +1.035875 0.171783447265625 0.486724853515625 0.6470092773437545 +1.036 0.17132568359375 0.485504150390625 0.6470092773437545 +1.036125 0.1708984375 0.484222412109375 0.6470092773437545 +1.0362500000000002 0.1708984375 0.484222412109375 0.6470092773437545 +1.036375 0.17041015625 0.48291015625 0.6470092773437545 +1.0365 0.17041015625 0.48291015625 0.6470092773437545 +1.0366250000000002 0.169921875 0.48150634765625 0.6470092773437545 +1.03675 0.16943359375 0.480072021484375 0.6470092773437545 +1.036875 0.16943359375 0.480072021484375 0.6470092773437545 +1.037 0.16888427734375 0.478607177734375 0.6470092773437545 +1.037125 0.16888427734375 0.478607177734375 0.6470092773437545 +1.03725 0.168365478515625 0.47705078125 0.6470092773437545 +1.037375 0.16778564453125 0.4754638671875 0.6470092773437545 +1.0375 0.16778564453125 0.4754638671875 0.6470092773437545 +1.037625 0.167205810546875 0.47381591796875 0.6470092773437545 +1.03775 0.167205810546875 0.47381591796875 0.6470092773437545 +1.037875 0.1666259765625 0.472137451171875 0.6470092773437545 +1.038 0.165985107421875 0.470367431640625 0.6470092773437545 +1.038125 0.165985107421875 0.470367431640625 0.6470092773437545 +1.0382500000000002 0.165374755859375 0.46856689453125 0.6470092773437545 +1.038375 0.165374755859375 0.46856689453125 0.6470092773437545 +1.0385 0.164703369140625 0.46673583984375 0.6470092773437545 +1.0386250000000002 0.164031982421875 0.464813232421875 0.6470092773437545 +1.03875 0.164031982421875 0.464813232421875 0.6470092773437545 +1.038875 0.163360595703125 0.462890625 0.6470092773437545 +1.039 0.163360595703125 0.462890625 0.6470092773437545 +1.039125 0.162628173828125 0.46087646484375 0.6470092773437545 +1.03925 0.16192626953125 0.458831787109375 0.6470092773437545 +1.039375 0.16192626953125 0.458831787109375 0.6470092773437545 +1.0395 0.161163330078125 0.45672607421875 0.6470092773437545 +1.039625 0.161163330078125 0.45672607421875 0.6470092773437545 +1.03975 0.160400390625 0.454559326171875 0.6470092773437545 +1.039875 0.159637451171875 0.452362060546875 0.6470092773437545 +1.04 0.159637451171875 0.452362060546875 0.6470092773437545 +1.040125 0.158843994140625 0.450103759765625 0.6470092773437545 +1.0402500000000002 0.158843994140625 0.450103759765625 0.6470092773437545 +1.040375 0.15802001953125 0.447784423828125 0.6470092773437545 +1.0405 0.157196044921875 0.4454345703125 0.6470092773437545 +1.040625 0.157196044921875 0.4454345703125 0.6470092773437545 +1.04075 0.156341552734375 0.44305419921875 0.6470092773437545 +1.040875 0.156341552734375 0.44305419921875 0.6470092773437545 +1.041 0.155487060546875 0.440582275390625 0.6470092773437545 +1.041125 0.15460205078125 0.438079833984375 0.6470092773437545 +1.04125 0.15460205078125 0.438079833984375 0.6470092773437545 +1.041375 0.153717041015625 0.435546875 0.6470092773437545 +1.0415 0.153717041015625 0.435546875 0.6470092773437545 +1.041625 0.152801513671875 0.432952880859375 0.6470092773437545 +1.04175 0.15185546875 0.4302978515625 0.6470092773437545 +1.041875 0.15185546875 0.4302978515625 0.6470092773437545 +1.042 0.150909423828125 0.4276123046875 0.6470092773437545 +1.042125 0.150909423828125 0.4276123046875 0.6470092773437545 +1.0422500000000002 0.149932861328125 0.424896240234375 0.6470092773437545 +1.042375 0.148956298828125 0.422119140625 0.6470092773437545 +1.0425 0.148956298828125 0.422119140625 0.6470092773437545 +1.042625 0.14794921875 0.419281005859375 0.6470092773437545 +1.04275 0.14794921875 0.419281005859375 0.6470092773437545 +1.042875 0.146942138671875 0.416412353515625 0.6470092773437545 +1.043 0.145904541015625 0.413482666015625 0.6470092773437545 +1.043125 0.145904541015625 0.413482666015625 0.6470092773437545 +1.04325 0.144866943359375 0.4105224609375 0.6470092773437545 +1.043375 0.144866943359375 0.4105224609375 0.6470092773437545 +1.0435 0.143798828125 0.407501220703125 0.6470092773437545 +1.043625 0.142730712890625 0.404449462890625 0.6470092773437545 +1.04375 0.142730712890625 0.404449462890625 0.6470092773437545 +1.043875 0.141632080078125 0.401336669921875 0.6470092773437545 +1.044 0.141632080078125 0.401336669921875 0.6470092773437545 +1.044125 0.140533447265625 0.398193359375 0.6470092773437545 +1.0442500000000002 0.139404296875 0.39501953125 0.6470092773437545 +1.044375 0.139404296875 0.39501953125 0.6470092773437545 +1.0445 0.138275146484375 0.39178466796875 0.6470092773437545 +1.044625 0.138275146484375 0.39178466796875 0.6470092773437545 +1.04475 0.137115478515625 0.388519287109375 0.6470092773437545 +1.044875 0.13592529296875 0.38519287109375 0.6470092773437545 +1.045 0.13592529296875 0.38519287109375 0.6470092773437545 +1.045125 0.134765625 0.3818359375 0.6470092773437545 +1.04525 0.134765625 0.3818359375 0.6470092773437545 +1.045375 0.133544921875 0.378448486328125 0.6470092773437545 +1.0455 0.13232421875 0.375 0.6470092773437545 +1.045625 0.13232421875 0.375 0.6470092773437545 +1.04575 0.131103515625 0.37152099609375 0.6470092773437545 +1.045875 0.131103515625 0.37152099609375 0.6470092773437545 +1.046 0.129852294921875 0.36798095703125 0.6470092773437545 +1.046125 0.12860107421875 0.36444091796875 0.6470092773437545 +1.0462500000000002 0.12860107421875 0.36444091796875 0.6470092773437545 +1.046375 0.1273193359375 0.360809326171875 0.6470092773437545 +1.0465 0.1273193359375 0.360809326171875 0.6470092773437545 +1.046625 0.12603759765625 0.357177734375 0.6470092773437545 +1.04675 0.124755859375 0.353485107421875 0.6470092773437545 +1.046875 0.124755859375 0.353485107421875 0.6470092773437545 +1.047 0.123443603515625 0.349761962890625 0.6470092773437545 +1.047125 0.123443603515625 0.349761962890625 0.6470092773437545 +1.04725 0.122100830078125 0.34600830078125 0.6470092773437545 +1.047375 0.120758056640625 0.34222412109375 0.6470092773437545 +1.0475 0.120758056640625 0.34222412109375 0.6470092773437545 +1.047625 0.119415283203125 0.33837890625 0.6470092773437545 +1.04775 0.119415283203125 0.33837890625 0.6470092773437545 +1.0478750000000002 0.1180419921875 0.334503173828125 0.6470092773437545 +1.048 0.116668701171875 0.330596923828125 0.6470092773437545 +1.048125 0.116668701171875 0.330596923828125 0.6470092773437545 +1.0482500000000002 0.115264892578125 0.32666015625 0.6470092773437545 +1.048375 0.115264892578125 0.32666015625 0.6470092773437545 +1.0485 0.113861083984375 0.322662353515625 0.6470092773437545 +1.048625 0.112457275390625 0.31866455078125 0.6470092773437545 +1.04875 0.112457275390625 0.31866455078125 0.6470092773437545 +1.048875 0.11102294921875 0.314605712890625 0.6470092773437545 +1.049 0.11102294921875 0.314605712890625 0.6470092773437545 +1.049125 0.109588623046875 0.310516357421875 0.6470092773437545 +1.04925 0.108123779296875 0.306396484375 0.6470092773437545 +1.049375 0.108123779296875 0.306396484375 0.6470092773437545 +1.0495 0.106658935546875 0.30224609375 0.6470092773437545 +1.049625 0.106658935546875 0.30224609375 0.6470092773437545 +1.04975 0.105194091796875 0.298065185546875 0.6470092773437545 +1.0498750000000002 0.10369873046875 0.2938232421875 0.6470092773437545 +1.05 0.10369873046875 0.2938232421875 0.6470092773437545 +1.050125 0.102203369140625 0.289581298828125 0.6470092773437545 +1.0502500000000002 0.102203369140625 0.289581298828125 0.6470092773437545 +1.050375 0.100677490234375 0.285308837890625 0.6470092773437545 +1.0505 0.099151611328125 0.280975341796875 0.6470092773437545 +1.050625 0.099151611328125 0.280975341796875 0.6470092773437545 +1.05075 0.097625732421875 0.276641845703125 0.6470092773437545 +1.050875 0.097625732421875 0.276641845703125 0.6470092773437545 +1.051 0.0960693359375 0.27227783203125 0.6470092773437545 +1.051125 0.094512939453125 0.267852783203125 0.6470092773437545 +1.05125 0.094512939453125 0.267852783203125 0.6470092773437545 +1.051375 0.09295654296875 0.263427734375 0.6470092773437545 +1.0515 0.09295654296875 0.263427734375 0.6470092773437545 +1.051625 0.091400146484375 0.25897216796875 0.6470092773437545 +1.05175 0.089813232421875 0.25445556640625 0.6470092773437545 +1.0518750000000002 0.089813232421875 0.25445556640625 0.6470092773437545 +1.052 0.08819580078125 0.249969482421875 0.6470092773437545 +1.052125 0.08819580078125 0.249969482421875 0.6470092773437545 +1.0522500000000002 0.08660888671875 0.24542236328125 0.6470092773437545 +1.052375 0.084991455078125 0.2408447265625 0.6470092773437545 +1.0525 0.084991455078125 0.2408447265625 0.6470092773437545 +1.052625 0.0833740234375 0.236236572265625 0.6470092773437545 +1.05275 0.0833740234375 0.236236572265625 0.6470092773437545 +1.052875 0.08172607421875 0.231597900390625 0.6470092773437545 +1.053 0.080078125 0.226959228515625 0.6470092773437545 +1.053125 0.080078125 0.226959228515625 0.6470092773437545 +1.05325 0.07843017578125 0.2222900390625 0.6470092773437545 +1.053375 0.07843017578125 0.2222900390625 0.6470092773437545 +1.0535 0.0767822265625 0.21759033203125 0.6470092773437545 +1.053625 0.075103759765625 0.212860107421875 0.6470092773437545 +1.05375 0.075103759765625 0.212860107421875 0.6470092773437545 +1.0538750000000002 0.07342529296875 0.208099365234375 0.6470092773437545 +1.054 0.07342529296875 0.208099365234375 0.6470092773437545 +1.054125 0.071746826171875 0.203338623046875 0.6470092773437545 +1.0542500000000002 0.070068359375 0.19854736328125 0.6470092773437545 +1.054375 0.070068359375 0.19854736328125 0.6470092773437545 +1.0545 0.068359375 0.1937255859375 0.6470092773437545 +1.054625 0.068359375 0.1937255859375 0.6470092773437545 +1.05475 0.066650390625 0.188873291015625 0.6470092773437545 +1.054875 0.06494140625 0.18402099609375 0.6470092773437545 +1.055 0.06494140625 0.18402099609375 0.6470092773437545 +1.055125 0.063201904296875 0.17913818359375 0.6470092773437545 +1.05525 0.063201904296875 0.17913818359375 0.6470092773437545 +1.055375 0.061492919921875 0.17425537109375 0.6470092773437545 +1.0555 0.05975341796875 0.169342041015625 0.6470092773437545 +1.055625 0.05975341796875 0.169342041015625 0.6470092773437545 +1.05575 0.058013916015625 0.164398193359375 0.6470092773437545 +1.0558750000000002 0.058013916015625 0.164398193359375 0.6470092773437545 +1.056 0.01690673828125 0.159454345703125 0.8937805175781277 +1.056125 0.016387939453125 0.15447998046875 0.8937805175781277 +1.05625 0.016387939453125 0.15447998046875 0.8937805175781277 +1.056375 0.015838623046875 0.14947509765625 0.8937805175781277 +1.0565 0.015838623046875 0.14947509765625 0.8937805175781277 +1.056625 0.01531982421875 0.14447021484375 0.8937805175781277 +1.05675 0.014801025390625 0.13946533203125 0.8937805175781277 +1.056875 0.014801025390625 0.13946533203125 0.8937805175781277 +1.057 0.014251708984375 0.134429931640625 0.8937805175781277 +1.057125 0.014251708984375 0.134429931640625 0.8937805175781277 +1.05725 0.01373291015625 0.129364013671875 0.8937805175781277 +1.057375 0.01318359375 0.124298095703125 0.8937805175781277 +1.0575 0.01318359375 0.124298095703125 0.8937805175781277 +1.057625 0.01263427734375 0.119232177734375 0.8937805175781277 +1.05775 0.01263427734375 0.119232177734375 0.8937805175781277 +1.0578750000000002 0.012115478515625 0.1141357421875 0.8937805175781277 +1.058 0.011566162109375 0.109039306640625 0.8937805175781277 +1.058125 0.011566162109375 0.109039306640625 0.8937805175781277 +1.05825 0.011016845703125 0.103912353515625 0.8937805175781277 +1.058375 0.011016845703125 0.103912353515625 0.8937805175781277 +1.0585 0.010467529296875 0.098785400390625 0.8937805175781277 +1.058625 0.009918212890625 0.093658447265625 0.8937805175781277 +1.05875 0.009918212890625 0.093658447265625 0.8937805175781277 +1.058875 0.009368896484375 0.0885009765625 0.8937805175781277 +1.059 0.009368896484375 0.0885009765625 0.8937805175781277 +1.059125 0.008819580078125 0.083343505859375 0.8937805175781277 +1.05925 0.008270263671875 0.07818603515625 0.8937805175781277 +1.059375 0.008270263671875 0.07818603515625 0.8937805175781277 +1.0595 0.007720947265625 0.072998046875 0.8937805175781277 +1.059625 0.007720947265625 0.072998046875 0.8937805175781277 +1.05975 0.007171630859375 0.06781005859375 0.8937805175781277 +1.0598750000000002 0.006622314453125 0.0626220703125 0.8937805175781277 +1.06 0.006622314453125 0.0626220703125 0.8937805175781277 +1.060125 0.006072998046875 0.05743408203125 0.8937805175781277 +1.06025 0.006072998046875 0.05743408203125 0.8937805175781277 +1.060375 0.005523681640625 0.05224609375 0.8937805175781277 +1.0605 0.004974365234375 0.047027587890625 0.8937805175781277 +1.060625 0.004974365234375 0.047027587890625 0.8937805175781277 +1.06075 0.004425048828125 0.04180908203125 0.8937805175781277 +1.060875 0.004425048828125 0.04180908203125 0.8937805175781277 +1.061 0.003875732421875 0.036590576171875 0.8937805175781277 +1.061125 0.003326416015625 0.0313720703125 0.8937805175781277 +1.06125 0.003326416015625 0.0313720703125 0.8937805175781277 +1.061375 0.00274658203125 0.026123046875 0.8937805175781277 +1.0615 0.00274658203125 0.026123046875 0.8937805175781277 +1.061625 0.002197265625 0.020904541015625 0.8937805175781277 +1.06175 0.00164794921875 0.01568603515625 0.8937805175781277 +1.0618750000000002 0.00164794921875 0.01568603515625 0.8937805175781277 +1.062 0.0010986328125 0.01043701171875 0.8937805175781277 +1.062125 0.0010986328125 0.01043701171875 0.8937805175781277 +1.06225 0.00054931640625 0.005218505859375 0.8937805175781277 +1.062375 0.0 0.0 0.8937805175781277 +1.0625 0.0 0.0 0.8937805175781277 +1.062625 -0.00054931640625 -0.005218505859375 0.8937805175781277 +1.06275 -0.00054931640625 -0.005218505859375 0.8937805175781277 +1.062875 -0.0010986328125 -0.01043701171875 0.8937805175781277 +1.063 -0.00164794921875 -0.01568603515625 0.8937805175781277 +1.063125 -0.00164794921875 -0.01568603515625 0.8937805175781277 +1.06325 -0.002197265625 -0.020904541015625 0.8937805175781277 +1.063375 -0.002197265625 -0.020904541015625 0.8937805175781277 +1.0635000000000002 -0.00274658203125 -0.026123046875 0.8937805175781277 +1.063625 -0.003326416015625 -0.0313720703125 0.8937805175781277 +1.06375 -0.003326416015625 -0.0313720703125 0.8937805175781277 +1.0638750000000002 -0.003875732421875 -0.036590576171875 0.8937805175781277 +1.064 -0.003875732421875 -0.036590576171875 0.8937805175781277 +1.064125 -0.004425048828125 -0.04180908203125 0.8937805175781277 +1.06425 -0.004974365234375 -0.047027587890625 0.8937805175781277 +1.064375 -0.004974365234375 -0.047027587890625 0.8937805175781277 +1.0645 -0.005523681640625 -0.05224609375 0.8937805175781277 +1.064625 -0.005523681640625 -0.05224609375 0.8937805175781277 +1.06475 -0.006072998046875 -0.05743408203125 0.8937805175781277 +1.064875 -0.006622314453125 -0.0626220703125 0.8937805175781277 +1.065 -0.006622314453125 -0.0626220703125 0.8937805175781277 +1.065125 -0.007171630859375 -0.06781005859375 0.8937805175781277 +1.06525 -0.007171630859375 -0.06781005859375 0.8937805175781277 +1.065375 -0.007720947265625 -0.072998046875 0.8937805175781277 +1.0655000000000002 -0.008270263671875 -0.07818603515625 0.8937805175781277 +1.065625 -0.008270263671875 -0.07818603515625 0.8937805175781277 +1.06575 -0.008819580078125 -0.083343505859375 0.8937805175781277 +1.0658750000000002 -0.008819580078125 -0.083343505859375 0.8937805175781277 +1.066 -0.009368896484375 -0.0885009765625 0.8937805175781277 +1.066125 -0.009918212890625 -0.093658447265625 0.8937805175781277 +1.06625 -0.009918212890625 -0.093658447265625 0.8937805175781277 +1.066375 -0.010467529296875 -0.098785400390625 0.8937805175781277 +1.0665 -0.010467529296875 -0.098785400390625 0.8937805175781277 +1.066625 -0.011016845703125 -0.103912353515625 0.8937805175781277 +1.06675 -0.011566162109375 -0.109039306640625 0.8937805175781277 +1.066875 -0.011566162109375 -0.109039306640625 0.8937805175781277 +1.067 -0.012115478515625 -0.1141357421875 0.8937805175781277 +1.067125 -0.012115478515625 -0.1141357421875 0.8937805175781277 +1.06725 -0.01263427734375 -0.119232177734375 0.8937805175781277 +1.067375 -0.01318359375 -0.124298095703125 0.8937805175781277 +1.0675000000000002 -0.01318359375 -0.124298095703125 0.8937805175781277 +1.067625 -0.01373291015625 -0.129364013671875 0.8937805175781277 +1.06775 -0.01373291015625 -0.129364013671875 0.8937805175781277 +1.0678750000000002 -0.014251708984375 -0.134429931640625 0.8937805175781277 +1.068 -0.014801025390625 -0.13946533203125 0.8937805175781277 +1.068125 -0.014801025390625 -0.13946533203125 0.8937805175781277 +1.06825 -0.01531982421875 -0.14447021484375 0.8937805175781277 +1.068375 -0.01531982421875 -0.14447021484375 0.8937805175781277 +1.0685 -0.015838623046875 -0.14947509765625 0.8937805175781277 +1.068625 -0.016387939453125 -0.15447998046875 0.8937805175781277 +1.06875 -0.016387939453125 -0.15447998046875 0.8937805175781277 +1.068875 -0.01690673828125 -0.159454345703125 0.8937805175781277 +1.069 -0.01690673828125 -0.159454345703125 0.8937805175781277 +1.069125 -0.017425537109375 -0.164398193359375 0.8937805175781277 +1.06925 -0.017974853515625 -0.169342041015625 0.8937805175781277 +1.069375 -0.017974853515625 -0.169342041015625 0.8937805175781277 +1.0695000000000002 -0.01849365234375 -0.17425537109375 0.8937805175781277 +1.069625 -0.01849365234375 -0.17425537109375 0.8937805175781277 +1.06975 -0.019012451171875 -0.17913818359375 0.8937805175781277 +1.0698750000000002 -0.01953125 -0.18402099609375 0.8937805175781277 +1.07 -0.01953125 -0.18402099609375 0.8937805175781277 +1.070125 -0.020050048828125 -0.188873291015625 0.8937805175781277 +1.07025 -0.020050048828125 -0.188873291015625 0.8937805175781277 +1.070375 -0.020538330078125 -0.1937255859375 0.8937805175781277 +1.0705 -0.02105712890625 -0.19854736328125 0.8937805175781277 +1.070625 -0.02105712890625 -0.19854736328125 0.8937805175781277 +1.07075 -0.021575927734375 -0.203338623046875 0.8937805175781277 +1.070875 -0.021575927734375 -0.203338623046875 0.8937805175781277 +1.071 -0.022064208984375 -0.208099365234375 0.8937805175781277 +1.071125 -0.0225830078125 -0.212860107421875 0.8937805175781277 +1.07125 -0.0225830078125 -0.212860107421875 0.8937805175781277 +1.071375 -0.0230712890625 -0.21759033203125 0.8937805175781277 +1.0715000000000002 -0.0230712890625 -0.21759033203125 0.8937805175781277 +1.071625 -0.023590087890625 -0.2222900390625 0.8937805175781277 +1.07175 -0.024078369140625 -0.226959228515625 0.8937805175781277 +1.0718750000000002 -0.024078369140625 -0.226959228515625 0.8937805175781277 +1.072 -0.024566650390625 -0.231597900390625 0.8937805175781277 +1.072125 -0.024566650390625 -0.231597900390625 0.8937805175781277 +1.07225 -0.025054931640625 -0.236236572265625 0.8937805175781277 +1.072375 -0.025543212890625 -0.2408447265625 0.8937805175781277 +1.0725 -0.025543212890625 -0.2408447265625 0.8937805175781277 +1.072625 -0.026031494140625 -0.24542236328125 0.8937805175781277 +1.07275 -0.026031494140625 -0.24542236328125 0.8937805175781277 +1.072875 -0.026519775390625 -0.249969482421875 0.8937805175781277 +1.073 -0.027008056640625 -0.25445556640625 0.8937805175781277 +1.073125 -0.027008056640625 -0.25445556640625 0.8937805175781277 +1.07325 -0.0274658203125 -0.25897216796875 0.8937805175781277 +1.073375 -0.0274658203125 -0.25897216796875 0.8937805175781277 +1.0735000000000002 -0.0279541015625 -0.263427734375 0.8937805175781277 +1.073625 -0.028411865234375 -0.267852783203125 0.8937805175781277 +1.07375 -0.028411865234375 -0.267852783203125 0.8937805175781277 +1.073875 -0.028900146484375 -0.27227783203125 0.8937805175781277 +1.074 -0.028900146484375 -0.27227783203125 0.8937805175781277 +1.074125 -0.02935791015625 -0.276641845703125 0.8937805175781277 +1.07425 -0.029815673828125 -0.280975341796875 0.8937805175781277 +1.074375 -0.029815673828125 -0.280975341796875 0.8937805175781277 +1.0745 -0.0302734375 -0.285308837890625 0.8937805175781277 +1.074625 -0.0302734375 -0.285308837890625 0.8937805175781277 +1.07475 -0.030731201171875 -0.289581298828125 0.8937805175781277 +1.074875 -0.03118896484375 -0.2938232421875 0.8937805175781277 +1.075 -0.03118896484375 -0.2938232421875 0.8937805175781277 +1.075125 -0.0316162109375 -0.298065185546875 0.8937805175781277 +1.07525 -0.0316162109375 -0.298065185546875 0.8937805175781277 +1.075375 -0.032073974609375 -0.30224609375 0.8937805175781277 +1.0755000000000002 -0.032501220703125 -0.306396484375 0.8937805175781277 +1.075625 -0.032501220703125 -0.306396484375 0.8937805175781277 +1.07575 -0.032958984375 -0.310516357421875 0.8937805175781277 +1.075875 -0.032958984375 -0.310516357421875 0.8937805175781277 +1.076 -0.03338623046875 -0.314605712890625 0.8937805175781277 +1.076125 -0.0338134765625 -0.31866455078125 0.8937805175781277 +1.07625 -0.0338134765625 -0.31866455078125 0.8937805175781277 +1.076375 -0.03424072265625 -0.322662353515625 0.8937805175781277 +1.0765 -0.03424072265625 -0.322662353515625 0.8937805175781277 +1.076625 -0.03466796875 -0.32666015625 0.8937805175781277 +1.07675 -0.035064697265625 -0.330596923828125 0.8937805175781277 +1.076875 -0.035064697265625 -0.330596923828125 0.8937805175781277 +1.077 -0.035491943359375 -0.334503173828125 0.8937805175781277 +1.077125 -0.035491943359375 -0.334503173828125 0.8937805175781277 +1.07725 -0.035919189453125 -0.33837890625 0.8937805175781277 +1.077375 -0.03631591796875 -0.34222412109375 0.8937805175781277 +1.0775000000000002 -0.03631591796875 -0.34222412109375 0.8937805175781277 +1.077625 -0.036712646484375 -0.34600830078125 0.8937805175781277 +1.07775 -0.036712646484375 -0.34600830078125 0.8937805175781277 +1.077875 -0.037109375 -0.349761962890625 0.8937805175781277 +1.078 -0.037506103515625 -0.353485107421875 0.8937805175781277 +1.078125 -0.037506103515625 -0.353485107421875 0.8937805175781277 +1.07825 -0.03790283203125 -0.357177734375 0.8937805175781277 +1.078375 -0.03790283203125 -0.357177734375 0.8937805175781277 +1.0785 -0.038299560546875 -0.360809326171875 0.8937805175781277 +1.078625 -0.038665771484375 -0.36444091796875 0.8937805175781277 +1.07875 -0.038665771484375 -0.36444091796875 0.8937805175781277 +1.078875 -0.039031982421875 -0.36798095703125 0.8937805175781277 +1.079 -0.039031982421875 -0.36798095703125 0.8937805175781277 +1.0791250000000002 -0.0394287109375 -0.37152099609375 0.8937805175781277 +1.07925 -0.039794921875 -0.375 0.8937805175781277 +1.079375 -0.039794921875 -0.375 0.8937805175781277 +1.0795000000000002 -0.0401611328125 -0.378448486328125 0.8937805175781277 +1.079625 -0.0401611328125 -0.378448486328125 0.8937805175781277 +1.07975 -0.04052734375 -0.3818359375 0.8937805175781277 +1.079875 -0.040863037109375 -0.38519287109375 0.8937805175781277 +1.08 -0.040863037109375 -0.38519287109375 0.8937805175781277 +1.080125 -0.041229248046875 -0.388519287109375 0.8937805175781277 +1.08025 -0.041229248046875 -0.388519287109375 0.8937805175781277 +1.080375 -0.04156494140625 -0.39178466796875 0.8937805175781277 +1.0805 -0.04193115234375 -0.39501953125 0.8937805175781277 +1.080625 -0.04193115234375 -0.39501953125 0.8937805175781277 +1.08075 -0.042266845703125 -0.398193359375 0.8937805175781277 +1.080875 -0.042266845703125 -0.398193359375 0.8937805175781277 +1.081 -0.0426025390625 -0.401336669921875 0.8937805175781277 +1.0811250000000002 -0.04290771484375 -0.404449462890625 0.8937805175781277 +1.08125 -0.04290771484375 -0.404449462890625 0.8937805175781277 +1.081375 -0.043243408203125 -0.407501220703125 0.8937805175781277 +1.0815000000000002 -0.043243408203125 -0.407501220703125 0.8937805175781277 +1.081625 -0.043548583984375 -0.4105224609375 0.8937805175781277 +1.08175 -0.04388427734375 -0.413482666015625 0.8937805175781277 +1.081875 -0.04388427734375 -0.413482666015625 0.8937805175781277 +1.082 -0.044189453125 -0.416412353515625 0.8937805175781277 +1.082125 -0.044189453125 -0.416412353515625 0.8937805175781277 +1.08225 -0.04449462890625 -0.419281005859375 0.8937805175781277 +1.082375 -0.0447998046875 -0.422119140625 0.8937805175781277 +1.0825 -0.0447998046875 -0.422119140625 0.8937805175781277 +1.082625 -0.045074462890625 -0.424896240234375 0.8937805175781277 +1.08275 -0.045074462890625 -0.424896240234375 0.8937805175781277 +1.082875 -0.045379638671875 -0.4276123046875 0.8937805175781277 +1.083 -0.045654296875 -0.4302978515625 0.8937805175781277 +1.0831250000000002 -0.045654296875 -0.4302978515625 0.8937805175781277 +1.08325 -0.045928955078125 -0.432952880859375 0.8937805175781277 +1.083375 -0.045928955078125 -0.432952880859375 0.8937805175781277 +1.0835000000000002 -0.04620361328125 -0.435546875 0.8937805175781277 +1.083625 -0.046478271484375 -0.438079833984375 0.8937805175781277 +1.08375 -0.046478271484375 -0.438079833984375 0.8937805175781277 +1.083875 -0.0467529296875 -0.440582275390625 0.8937805175781277 +1.084 -0.0467529296875 -0.440582275390625 0.8937805175781277 +1.084125 -0.047027587890625 -0.44305419921875 0.8937805175781277 +1.08425 -0.047271728515625 -0.4454345703125 0.8937805175781277 +1.084375 -0.047271728515625 -0.4454345703125 0.8937805175781277 +1.0845 -0.047515869140625 -0.447784423828125 0.8937805175781277 +1.084625 -0.047515869140625 -0.447784423828125 0.8937805175781277 +1.08475 -0.047760009765625 -0.450103759765625 0.8937805175781277 +1.084875 -0.048004150390625 -0.452362060546875 0.8937805175781277 +1.085 -0.048004150390625 -0.452362060546875 0.8937805175781277 +1.0851250000000002 -0.048248291015625 -0.454559326171875 0.8937805175781277 +1.08525 -0.048248291015625 -0.454559326171875 0.8937805175781277 +1.085375 -0.0484619140625 -0.45672607421875 0.8937805175781277 +1.0855000000000002 -0.048675537109375 -0.458831787109375 0.8937805175781277 +1.085625 -0.048675537109375 -0.458831787109375 0.8937805175781277 +1.08575 -0.048919677734375 -0.46087646484375 0.8937805175781277 +1.085875 -0.048919677734375 -0.46087646484375 0.8937805175781277 +1.086 -0.04913330078125 -0.462890625 0.8937805175781277 +1.086125 -0.04931640625 -0.464813232421875 0.8937805175781277 +1.08625 -0.04931640625 -0.464813232421875 0.8937805175781277 +1.086375 -0.049530029296875 -0.46673583984375 0.8937805175781277 +1.0865 -0.049530029296875 -0.46673583984375 0.8937805175781277 +1.086625 -0.049713134765625 -0.46856689453125 0.8937805175781277 +1.08675 -0.0499267578125 -0.470367431640625 0.8937805175781277 +1.086875 -0.0499267578125 -0.470367431640625 0.8937805175781277 +1.087 -0.05010986328125 -0.472137451171875 0.8937805175781277 +1.0871250000000002 -0.05010986328125 -0.472137451171875 0.8937805175781277 +1.08725 -0.05029296875 -0.47381591796875 0.8937805175781277 +1.087375 -0.050445556640625 -0.4754638671875 0.8937805175781277 +1.0875000000000002 -0.050445556640625 -0.4754638671875 0.8937805175781277 +1.087625 -0.050628662109375 -0.47705078125 0.8937805175781277 +1.08775 -0.050628662109375 -0.47705078125 0.8937805175781277 +1.087875 -0.05078125 -0.478607177734375 0.8937805175781277 +1.088 -0.00091552734375 -0.480072021484375 0.9979858398437503488 +1.088125 -0.00091552734375 -0.480072021484375 0.9979858398437503488 +1.08825 -0.00091552734375 -0.48150634765625 0.9979858398437503488 +1.088375 -0.00091552734375 -0.48150634765625 0.9979858398437503488 +1.0885 -0.00091552734375 -0.48291015625 0.9979858398437503488 +1.088625 -0.00091552734375 -0.484222412109375 0.9979858398437503488 +1.08875 -0.00091552734375 -0.484222412109375 0.9979858398437503488 +1.088875 -0.00091552734375 -0.485504150390625 0.9979858398437503488 +1.089 -0.00091552734375 -0.485504150390625 0.9979858398437503488 +1.0891250000000002 -0.00091552734375 -0.486724853515625 0.9979858398437503488 +1.08925 -0.00091552734375 -0.487884521484375 0.9979858398437503488 +1.089375 -0.00091552734375 -0.487884521484375 0.9979858398437503488 +1.0895 -0.00091552734375 -0.489013671875 0.9979858398437503488 +1.089625 -0.00091552734375 -0.489013671875 0.9979858398437503488 +1.08975 -0.00091552734375 -0.490081787109375 0.9979858398437503488 +1.089875 -0.00091552734375 -0.4910888671875 0.9979858398437503488 +1.09 -0.00091552734375 -0.4910888671875 0.9979858398437503488 +1.090125 -0.000946044921875 -0.492034912109375 0.9979858398437503488 +1.09025 -0.000946044921875 -0.492034912109375 0.9979858398437503488 +1.090375 -0.000946044921875 -0.492950439453125 0.9979858398437503488 +1.0905 -0.000946044921875 -0.4937744140625 0.9979858398437503488 +1.090625 -0.000946044921875 -0.4937744140625 0.9979858398437503488 +1.09075 -0.000946044921875 -0.49456787109375 0.9979858398437503488 +1.090875 -0.000946044921875 -0.49456787109375 0.9979858398437503488 +1.091 -0.000946044921875 -0.49530029296875 0.9979858398437503488 +1.0911250000000002 -0.000946044921875 -0.496002197265625 0.9979858398437503488 +1.09125 -0.000946044921875 -0.496002197265625 0.9979858398437503488 +1.091375 -0.000946044921875 -0.496612548828125 0.9979858398437503488 +1.0915 -0.000946044921875 -0.496612548828125 0.9979858398437503488 +1.091625 -0.000946044921875 -0.4971923828125 0.9979858398437503488 +1.09175 -0.000946044921875 -0.497711181640625 0.9979858398437503488 +1.091875 -0.000946044921875 -0.497711181640625 0.9979858398437503488 +1.092 -0.000946044921875 -0.498199462890625 0.9979858398437503488 +1.092125 -0.000946044921875 -0.498199462890625 0.9979858398437503488 +1.09225 -0.000946044921875 -0.49859619140625 0.9979858398437503488 +1.092375 -0.000946044921875 -0.49896240234375 0.9979858398437503488 +1.0925 -0.000946044921875 -0.49896240234375 0.9979858398437503488 +1.092625 -0.000946044921875 -0.499267578125 0.9979858398437503488 +1.09275 -0.000946044921875 -0.499267578125 0.9979858398437503488 +1.092875 -0.000946044921875 -0.49951171875 0.9979858398437503488 +1.093 -0.000946044921875 -0.49969482421875 0.9979858398437503488 +1.0931250000000002 -0.000946044921875 -0.49969482421875 0.9979858398437503488 +1.09325 -0.000946044921875 -0.49981689453125 0.9979858398437503488 +1.093375 -0.000946044921875 -0.49981689453125 0.9979858398437503488 +1.0935 -0.000946044921875 -0.499908447265625 0.9979858398437503488 +1.093625 -0.000946044921875 -0.49993896484375 0.9979858398437503488 +1.09375 -0.000946044921875 -0.49993896484375 0.9979858398437503488 +1.093875 -0.000946044921875 -0.499908447265625 0.9979858398437503488 +1.094 -0.000946044921875 -0.499908447265625 0.9979858398437503488 +1.094125 -0.000946044921875 -0.49981689453125 0.9979858398437503488 +1.09425 -0.000946044921875 -0.49969482421875 0.9979858398437503488 +1.094375 -0.000946044921875 -0.49969482421875 0.9979858398437503488 +1.0945 -0.000946044921875 -0.49951171875 0.9979858398437503488 +1.094625 -0.000946044921875 -0.49951171875 0.9979858398437503488 +1.0947500000000002 -0.000946044921875 -0.499267578125 0.9979858398437503488 +1.094875 -0.000946044921875 -0.49896240234375 0.9979858398437503488 +1.095 -0.000946044921875 -0.49896240234375 0.9979858398437503488 +1.0951250000000002 -0.000946044921875 -0.49859619140625 0.9979858398437503488 +1.09525 -0.000946044921875 -0.49859619140625 0.9979858398437503488 +1.095375 -0.000946044921875 -0.498199462890625 0.9979858398437503488 +1.0955 -0.000946044921875 -0.497711181640625 0.9979858398437503488 +1.095625 -0.000946044921875 -0.497711181640625 0.9979858398437503488 +1.09575 -0.000946044921875 -0.4971923828125 0.9979858398437503488 +1.095875 -0.000946044921875 -0.4971923828125 0.9979858398437503488 +1.096 -0.000946044921875 -0.496612548828125 0.9979858398437503488 +1.096125 -0.000946044921875 -0.496002197265625 0.9979858398437503488 +1.09625 -0.000946044921875 -0.496002197265625 0.9979858398437503488 +1.096375 -0.000946044921875 -0.49530029296875 0.9979858398437503488 +1.0965 -0.000946044921875 -0.49530029296875 0.9979858398437503488 +1.096625 -0.000946044921875 -0.49456787109375 0.9979858398437503488 +1.0967500000000002 -0.000946044921875 -0.4937744140625 0.9979858398437503488 +1.096875 -0.000946044921875 -0.4937744140625 0.9979858398437503488 +1.097 -0.000946044921875 -0.492950439453125 0.9979858398437503488 +1.0971250000000002 -0.000946044921875 -0.492950439453125 0.9979858398437503488 +1.09725 -0.000946044921875 -0.492034912109375 0.9979858398437503488 +1.097375 -0.00091552734375 -0.4910888671875 0.9979858398437503488 +1.0975 -0.00091552734375 -0.4910888671875 0.9979858398437503488 +1.097625 -0.00091552734375 -0.490081787109375 0.9979858398437503488 +1.09775 -0.00091552734375 -0.490081787109375 0.9979858398437503488 +1.097875 -0.00091552734375 -0.489013671875 0.9979858398437503488 +1.098 -0.00091552734375 -0.487884521484375 0.9979858398437503488 +1.098125 -0.00091552734375 -0.487884521484375 0.9979858398437503488 +1.09825 -0.00091552734375 -0.486724853515625 0.9979858398437503488 +1.098375 -0.00091552734375 -0.486724853515625 0.9979858398437503488 +1.0985 -0.00091552734375 -0.485504150390625 0.9979858398437503488 +1.098625 -0.00091552734375 -0.484222412109375 0.9979858398437503488 +1.0987500000000002 -0.00091552734375 -0.484222412109375 0.9979858398437503488 +1.098875 -0.00091552734375 -0.48291015625 0.9979858398437503488 +1.099 -0.00091552734375 -0.48291015625 0.9979858398437503488 +1.0991250000000002 -0.00091552734375 -0.48150634765625 0.9979858398437503488 +1.09925 -0.00091552734375 -0.480072021484375 0.9979858398437503488 +1.099375 -0.00091552734375 -0.480072021484375 0.9979858398437503488 +1.0995 -0.00091552734375 -0.478607177734375 0.9979858398437503488 +1.099625 -0.00091552734375 -0.478607177734375 0.9979858398437503488 +1.09975 -0.00091552734375 -0.47705078125 0.9979858398437503488 +1.099875 -0.000885009765625 -0.4754638671875 0.9979858398437503488 +1.1 -0.000885009765625 -0.4754638671875 0.9979858398437503488 +1.100125 -0.000885009765625 -0.47381591796875 0.9979858398437503488 +1.10025 -0.000885009765625 -0.47381591796875 0.9979858398437503488 +1.100375 -0.000885009765625 -0.472137451171875 0.9979858398437503488 +1.1005 -0.000885009765625 -0.470367431640625 0.9979858398437503488 +1.100625 -0.000885009765625 -0.470367431640625 0.9979858398437503488 +1.1007500000000002 -0.000885009765625 -0.46856689453125 0.9979858398437503488 +1.100875 -0.000885009765625 -0.46856689453125 0.9979858398437503488 +1.101 -0.000885009765625 -0.46673583984375 0.9979858398437503488 +1.1011250000000002 -0.000885009765625 -0.464813232421875 0.9979858398437503488 +1.10125 -0.000885009765625 -0.464813232421875 0.9979858398437503488 +1.101375 -0.000885009765625 -0.462890625 0.9979858398437503488 +1.1015 -0.000885009765625 -0.462890625 0.9979858398437503488 +1.101625 -0.000885009765625 -0.46087646484375 0.9979858398437503488 +1.10175 -0.0008544921875 -0.458831787109375 0.9979858398437503488 +1.101875 -0.0008544921875 -0.458831787109375 0.9979858398437503488 +1.102 -0.0008544921875 -0.45672607421875 0.9979858398437503488 +1.102125 -0.0008544921875 -0.45672607421875 0.9979858398437503488 +1.10225 -0.0008544921875 -0.454559326171875 0.9979858398437503488 +1.102375 -0.0008544921875 -0.452362060546875 0.9979858398437503488 +1.1025 -0.0008544921875 -0.452362060546875 0.9979858398437503488 +1.102625 -0.0008544921875 -0.450103759765625 0.9979858398437503488 +1.1027500000000002 -0.0008544921875 -0.450103759765625 0.9979858398437503488 +1.102875 -0.0008544921875 -0.447784423828125 0.9979858398437503488 +1.103 -0.0008544921875 -0.4454345703125 0.9979858398437503488 +1.1031250000000002 -0.0008544921875 -0.4454345703125 0.9979858398437503488 +1.10325 -0.000823974609375 -0.44305419921875 0.9979858398437503488 +1.103375 -0.000823974609375 -0.44305419921875 0.9979858398437503488 +1.1035 -0.000823974609375 -0.440582275390625 0.9979858398437503488 +1.103625 -0.000823974609375 -0.438079833984375 0.9979858398437503488 +1.10375 -0.000823974609375 -0.438079833984375 0.9979858398437503488 +1.103875 -0.000823974609375 -0.435546875 0.9979858398437503488 +1.104 -0.000823974609375 -0.435546875 0.9979858398437503488 +1.104125 -0.000823974609375 -0.432952880859375 0.9979858398437503488 +1.10425 -0.000823974609375 -0.4302978515625 0.9979858398437503488 +1.104375 -0.000823974609375 -0.4302978515625 0.9979858398437503488 +1.1045 -0.00079345703125 -0.4276123046875 0.9979858398437503488 +1.104625 -0.00079345703125 -0.4276123046875 0.9979858398437503488 +1.1047500000000002 -0.00079345703125 -0.424896240234375 0.9979858398437503488 +1.104875 -0.00079345703125 -0.422119140625 0.9979858398437503488 +1.105 -0.00079345703125 -0.422119140625 0.9979858398437503488 +1.105125 -0.00079345703125 -0.419281005859375 0.9979858398437503488 +1.10525 -0.00079345703125 -0.419281005859375 0.9979858398437503488 +1.105375 -0.00079345703125 -0.416412353515625 0.9979858398437503488 +1.1055 -0.00079345703125 -0.413482666015625 0.9979858398437503488 +1.105625 -0.00079345703125 -0.413482666015625 0.9979858398437503488 +1.10575 -0.000762939453125 -0.4105224609375 0.9979858398437503488 +1.105875 -0.000762939453125 -0.4105224609375 0.9979858398437503488 +1.106 -0.000762939453125 -0.407501220703125 0.9979858398437503488 +1.106125 -0.000762939453125 -0.404449462890625 0.9979858398437503488 +1.10625 -0.000762939453125 -0.404449462890625 0.9979858398437503488 +1.106375 -0.000762939453125 -0.401336669921875 0.9979858398437503488 +1.1065 -0.000762939453125 -0.401336669921875 0.9979858398437503488 +1.106625 -0.000762939453125 -0.398193359375 0.9979858398437503488 +1.1067500000000002 -0.000732421875 -0.39501953125 0.9979858398437503488 +1.106875 -0.000732421875 -0.39501953125 0.9979858398437503488 +1.107 -0.000732421875 -0.39178466796875 0.9979858398437503488 +1.107125 -0.000732421875 -0.39178466796875 0.9979858398437503488 +1.10725 -0.000732421875 -0.388519287109375 0.9979858398437503488 +1.107375 -0.000732421875 -0.38519287109375 0.9979858398437503488 +1.1075 -0.000732421875 -0.38519287109375 0.9979858398437503488 +1.107625 -0.000732421875 -0.3818359375 0.9979858398437503488 +1.10775 -0.000732421875 -0.3818359375 0.9979858398437503488 +1.107875 -0.000701904296875 -0.378448486328125 0.9979858398437503488 +1.108 -0.000701904296875 -0.375 0.9979858398437503488 +1.108125 -0.000701904296875 -0.375 0.9979858398437503488 +1.10825 -0.000701904296875 -0.37152099609375 0.9979858398437503488 +1.108375 -0.000701904296875 -0.37152099609375 0.9979858398437503488 +1.1085 -0.000701904296875 -0.36798095703125 0.9979858398437503488 +1.108625 -0.00067138671875 -0.36444091796875 0.9979858398437503488 +1.1087500000000002 -0.00067138671875 -0.36444091796875 0.9979858398437503488 +1.108875 -0.00067138671875 -0.360809326171875 0.9979858398437503488 +1.109 -0.00067138671875 -0.360809326171875 0.9979858398437503488 +1.109125 -0.00067138671875 -0.357177734375 0.9979858398437503488 +1.10925 -0.00067138671875 -0.353485107421875 0.9979858398437503488 +1.109375 -0.00067138671875 -0.353485107421875 0.9979858398437503488 +1.1095 -0.00067138671875 -0.349761962890625 0.9979858398437503488 +1.109625 -0.00067138671875 -0.349761962890625 0.9979858398437503488 +1.10975 -0.000640869140625 -0.34600830078125 0.9979858398437503488 +1.109875 -0.000640869140625 -0.34222412109375 0.9979858398437503488 +1.11 -0.000640869140625 -0.34222412109375 0.9979858398437503488 +1.110125 -0.000640869140625 -0.33837890625 0.9979858398437503488 +1.11025 -0.000640869140625 -0.33837890625 0.9979858398437503488 +1.1103750000000002 -0.000640869140625 -0.334503173828125 0.9979858398437503488 +1.1105 -0.0006103515625 -0.330596923828125 0.9979858398437503488 +1.110625 -0.0006103515625 -0.330596923828125 0.9979858398437503488 +1.1107500000000002 -0.0006103515625 -0.32666015625 0.9979858398437503488 +1.110875 -0.0006103515625 -0.32666015625 0.9979858398437503488 +1.111 -0.0006103515625 -0.322662353515625 0.9979858398437503488 +1.111125 -0.0006103515625 -0.31866455078125 0.9979858398437503488 +1.11125 -0.0006103515625 -0.31866455078125 0.9979858398437503488 +1.111375 -0.000579833984375 -0.314605712890625 0.9979858398437503488 +1.1115 -0.000579833984375 -0.314605712890625 0.9979858398437503488 +1.111625 -0.000579833984375 -0.310516357421875 0.9979858398437503488 +1.11175 -0.000579833984375 -0.306396484375 0.9979858398437503488 +1.111875 -0.000579833984375 -0.306396484375 0.9979858398437503488 +1.112 -0.000579833984375 -0.30224609375 0.9979858398437503488 +1.112125 -0.000579833984375 -0.30224609375 0.9979858398437503488 +1.11225 -0.00054931640625 -0.298065185546875 0.9979858398437503488 +1.1123750000000002 -0.00054931640625 -0.2938232421875 0.9979858398437503488 +1.1125 -0.00054931640625 -0.2938232421875 0.9979858398437503488 +1.112625 -0.00054931640625 -0.289581298828125 0.9979858398437503488 +1.1127500000000002 -0.00054931640625 -0.289581298828125 0.9979858398437503488 +1.112875 -0.000518798828125 -0.285308837890625 0.9979858398437503488 +1.113 -0.000518798828125 -0.280975341796875 0.9979858398437503488 +1.113125 -0.000518798828125 -0.280975341796875 0.9979858398437503488 +1.11325 -0.000518798828125 -0.276641845703125 0.9979858398437503488 +1.113375 -0.000518798828125 -0.276641845703125 0.9979858398437503488 +1.1135 -0.000518798828125 -0.27227783203125 0.9979858398437503488 +1.113625 -0.00048828125 -0.267852783203125 0.9979858398437503488 +1.11375 -0.00048828125 -0.267852783203125 0.9979858398437503488 +1.113875 -0.00048828125 -0.263427734375 0.9979858398437503488 +1.114 -0.00048828125 -0.263427734375 0.9979858398437503488 +1.114125 -0.00048828125 -0.25897216796875 0.9979858398437503488 +1.11425 -0.00048828125 -0.25445556640625 0.9979858398437503488 +1.1143750000000002 -0.00048828125 -0.25445556640625 0.9979858398437503488 +1.1145 -0.000457763671875 -0.249969482421875 0.9979858398437503488 +1.114625 -0.000457763671875 -0.249969482421875 0.9979858398437503488 +1.1147500000000002 -0.000457763671875 -0.24542236328125 0.9979858398437503488 +1.114875 -0.000457763671875 -0.2408447265625 0.9979858398437503488 +1.115 -0.000457763671875 -0.2408447265625 0.9979858398437503488 +1.115125 -0.00042724609375 -0.236236572265625 0.9979858398437503488 +1.11525 -0.00042724609375 -0.236236572265625 0.9979858398437503488 +1.115375 -0.00042724609375 -0.231597900390625 0.9979858398437503488 +1.1155 -0.00042724609375 -0.226959228515625 0.9979858398437503488 +1.115625 -0.00042724609375 -0.226959228515625 0.9979858398437503488 +1.11575 -0.00042724609375 -0.2222900390625 0.9979858398437503488 +1.115875 -0.00042724609375 -0.2222900390625 0.9979858398437503488 +1.116 -0.000396728515625 -0.21759033203125 0.9979858398437503488 +1.116125 -0.000396728515625 -0.212860107421875 0.9979858398437503488 +1.11625 -0.000396728515625 -0.212860107421875 0.9979858398437503488 +1.1163750000000002 -0.000396728515625 -0.208099365234375 0.9979858398437503488 +1.1165 -0.000396728515625 -0.208099365234375 0.9979858398437503488 +1.116625 -0.0003662109375 -0.203338623046875 0.9979858398437503488 +1.1167500000000002 -0.0003662109375 -0.19854736328125 0.9979858398437503488 +1.116875 -0.0003662109375 -0.19854736328125 0.9979858398437503488 +1.117 -0.0003662109375 -0.1937255859375 0.9979858398437503488 +1.117125 -0.0003662109375 -0.1937255859375 0.9979858398437503488 +1.11725 -0.000335693359375 -0.188873291015625 0.9979858398437503488 +1.117375 -0.000335693359375 -0.18402099609375 0.9979858398437503488 +1.1175 -0.000335693359375 -0.18402099609375 0.9979858398437503488 +1.117625 -0.000335693359375 -0.17913818359375 0.9979858398437503488 +1.11775 -0.000335693359375 -0.17913818359375 0.9979858398437503488 +1.117875 -0.00030517578125 -0.17425537109375 0.9979858398437503488 +1.118 -0.00030517578125 -0.169342041015625 0.9979858398437503488 +1.118125 -0.00030517578125 -0.169342041015625 0.9979858398437503488 +1.11825 -0.00030517578125 -0.164398193359375 0.9979858398437503488 +1.1183750000000002 -0.00030517578125 -0.164398193359375 0.9979858398437503488 +1.1185 -0.00030517578125 -0.159454345703125 0.9979858398437503488 +1.118625 -0.000274658203125 -0.15447998046875 0.9979858398437503488 +1.1187500000000002 -0.000274658203125 -0.15447998046875 0.9979858398437503488 +1.118875 -0.000274658203125 -0.14947509765625 0.9979858398437503488 +1.119 -0.000274658203125 -0.14947509765625 0.9979858398437503488 +1.119125 -0.000274658203125 -0.14447021484375 0.9979858398437503488 +1.11925 -0.000244140625 -0.13946533203125 0.9979858398437503488 +1.119375 -0.000244140625 -0.13946533203125 0.9979858398437503488 +1.1195 -0.000244140625 -0.134429931640625 0.9979858398437503488 +1.119625 -0.000244140625 -0.134429931640625 0.9979858398437503488 +1.11975 -0.000244140625 -0.129364013671875 0.9979858398437503488 +1.119875 -0.000213623046875 -0.124298095703125 0.9979858398437503488 +1.12 -0.007080078125 -0.124298095703125 0.942932128906248 +1.120125 -0.00677490234375 -0.119232177734375 0.942932128906248 +1.12025 -0.00677490234375 -0.119232177734375 0.942932128906248 +1.1203750000000002 -0.006500244140625 -0.1141357421875 0.942932128906248 +1.1205 -0.006195068359375 -0.109039306640625 0.942932128906248 +1.120625 -0.006195068359375 -0.109039306640625 0.942932128906248 +1.12075 -0.00592041015625 -0.103912353515625 0.942932128906248 +1.120875 -0.00592041015625 -0.103912353515625 0.942932128906248 +1.121 -0.005615234375 -0.098785400390625 0.942932128906248 +1.121125 -0.00531005859375 -0.093658447265625 0.942932128906248 +1.12125 -0.00531005859375 -0.093658447265625 0.942932128906248 +1.121375 -0.005035400390625 -0.0885009765625 0.942932128906248 +1.1215 -0.005035400390625 -0.0885009765625 0.942932128906248 +1.121625 -0.004730224609375 -0.083343505859375 0.942932128906248 +1.12175 -0.00445556640625 -0.07818603515625 0.942932128906248 +1.121875 -0.00445556640625 -0.07818603515625 0.942932128906248 +1.122 -0.004150390625 -0.072998046875 0.942932128906248 +1.122125 -0.004150390625 -0.072998046875 0.942932128906248 +1.12225 -0.00384521484375 -0.06781005859375 0.942932128906248 +1.1223750000000002 -0.0035400390625 -0.0626220703125 0.942932128906248 +1.1225 -0.0035400390625 -0.0626220703125 0.942932128906248 +1.122625 -0.003265380859375 -0.05743408203125 0.942932128906248 +1.12275 -0.003265380859375 -0.05743408203125 0.942932128906248 +1.122875 -0.002960205078125 -0.05224609375 0.942932128906248 +1.123 -0.002655029296875 -0.047027587890625 0.942932128906248 +1.123125 -0.002655029296875 -0.047027587890625 0.942932128906248 +1.12325 -0.00238037109375 -0.04180908203125 0.942932128906248 +1.123375 -0.00238037109375 -0.04180908203125 0.942932128906248 +1.1235 -0.0020751953125 -0.036590576171875 0.942932128906248 +1.123625 -0.00177001953125 -0.0313720703125 0.942932128906248 +1.12375 -0.00177001953125 -0.0313720703125 0.942932128906248 +1.123875 -0.00146484375 -0.026123046875 0.942932128906248 +1.124 -0.00146484375 -0.026123046875 0.942932128906248 +1.124125 -0.001190185546875 -0.020904541015625 0.942932128906248 +1.12425 -0.000885009765625 -0.01568603515625 0.942932128906248 +1.1243750000000002 -0.000885009765625 -0.01568603515625 0.942932128906248 +1.1245 -0.000579833984375 -0.01043701171875 0.942932128906248 +1.124625 -0.000579833984375 -0.01043701171875 0.942932128906248 +1.12475 -0.000274658203125 -0.005218505859375 0.942932128906248 +1.124875 0.0 0.0 0.942932128906248 +1.125 0.0 0.0 0.942932128906248 +1.125125 0.000274658203125 0.005218505859375 0.942932128906248 +1.12525 0.000274658203125 0.005218505859375 0.942932128906248 +1.125375 0.000579833984375 0.01043701171875 0.942932128906248 +1.1255 0.000885009765625 0.01568603515625 0.942932128906248 +1.125625 0.000885009765625 0.01568603515625 0.942932128906248 +1.12575 0.001190185546875 0.020904541015625 0.942932128906248 +1.125875 0.001190185546875 0.020904541015625 0.942932128906248 +1.1260000000000002 0.00146484375 0.026123046875 0.942932128906248 +1.126125 0.00177001953125 0.0313720703125 0.942932128906248 +1.12625 0.00177001953125 0.0313720703125 0.942932128906248 +1.1263750000000002 0.0020751953125 0.036590576171875 0.942932128906248 +1.1265 0.0020751953125 0.036590576171875 0.942932128906248 +1.126625 0.00238037109375 0.04180908203125 0.942932128906248 +1.12675 0.002655029296875 0.047027587890625 0.942932128906248 +1.126875 0.002655029296875 0.047027587890625 0.942932128906248 +1.127 0.002960205078125 0.05224609375 0.942932128906248 +1.127125 0.002960205078125 0.05224609375 0.942932128906248 +1.12725 0.003265380859375 0.05743408203125 0.942932128906248 +1.127375 0.0035400390625 0.0626220703125 0.942932128906248 +1.1275 0.0035400390625 0.0626220703125 0.942932128906248 +1.127625 0.00384521484375 0.06781005859375 0.942932128906248 +1.12775 0.00384521484375 0.06781005859375 0.942932128906248 +1.127875 0.004150390625 0.072998046875 0.942932128906248 +1.1280000000000002 0.00445556640625 0.07818603515625 0.942932128906248 +1.128125 0.00445556640625 0.07818603515625 0.942932128906248 +1.12825 0.004730224609375 0.083343505859375 0.942932128906248 +1.1283750000000002 0.004730224609375 0.083343505859375 0.942932128906248 +1.1285 0.005035400390625 0.0885009765625 0.942932128906248 +1.128625 0.00531005859375 0.093658447265625 0.942932128906248 +1.12875 0.00531005859375 0.093658447265625 0.942932128906248 +1.128875 0.005615234375 0.098785400390625 0.942932128906248 +1.129 0.005615234375 0.098785400390625 0.942932128906248 +1.129125 0.00592041015625 0.103912353515625 0.942932128906248 +1.12925 0.006195068359375 0.109039306640625 0.942932128906248 +1.129375 0.006195068359375 0.109039306640625 0.942932128906248 +1.1295 0.006500244140625 0.1141357421875 0.942932128906248 +1.129625 0.006500244140625 0.1141357421875 0.942932128906248 +1.12975 0.00677490234375 0.119232177734375 0.942932128906248 +1.129875 0.007080078125 0.124298095703125 0.942932128906248 +1.1300000000000002 0.007080078125 0.124298095703125 0.942932128906248 +1.130125 0.007354736328125 0.129364013671875 0.942932128906248 +1.13025 0.007354736328125 0.129364013671875 0.942932128906248 +1.1303750000000002 0.007659912109375 0.134429931640625 0.942932128906248 +1.1305 0.0079345703125 0.13946533203125 0.942932128906248 +1.130625 0.0079345703125 0.13946533203125 0.942932128906248 +1.13075 0.008209228515625 0.14447021484375 0.942932128906248 +1.130875 0.008209228515625 0.14447021484375 0.942932128906248 +1.131 0.008514404296875 0.14947509765625 0.942932128906248 +1.131125 0.0087890625 0.15447998046875 0.942932128906248 +1.13125 0.0087890625 0.15447998046875 0.942932128906248 +1.131375 0.009063720703125 0.159454345703125 0.942932128906248 +1.1315 0.009063720703125 0.159454345703125 0.942932128906248 +1.131625 0.00933837890625 0.164398193359375 0.942932128906248 +1.13175 0.0096435546875 0.169342041015625 0.942932128906248 +1.131875 0.0096435546875 0.169342041015625 0.942932128906248 +1.1320000000000002 0.009918212890625 0.17425537109375 0.942932128906248 +1.132125 0.009918212890625 0.17425537109375 0.942932128906248 +1.13225 0.01019287109375 0.17913818359375 0.942932128906248 +1.1323750000000002 0.010467529296875 0.18402099609375 0.942932128906248 +1.1325 0.010467529296875 0.18402099609375 0.942932128906248 +1.132625 0.0107421875 0.188873291015625 0.942932128906248 +1.13275 0.0107421875 0.188873291015625 0.942932128906248 +1.132875 0.011016845703125 0.1937255859375 0.942932128906248 +1.133 0.01129150390625 0.19854736328125 0.942932128906248 +1.133125 0.01129150390625 0.19854736328125 0.942932128906248 +1.13325 0.011566162109375 0.203338623046875 0.942932128906248 +1.133375 0.011566162109375 0.203338623046875 0.942932128906248 +1.1335 0.0118408203125 0.208099365234375 0.942932128906248 +1.133625 0.012115478515625 0.212860107421875 0.942932128906248 +1.13375 0.012115478515625 0.212860107421875 0.942932128906248 +1.133875 0.01239013671875 0.21759033203125 0.942932128906248 +1.1340000000000002 0.01239013671875 0.21759033203125 0.942932128906248 +1.134125 0.012664794921875 0.2222900390625 0.942932128906248 +1.13425 0.012908935546875 0.226959228515625 0.942932128906248 +1.1343750000000002 0.012908935546875 0.226959228515625 0.942932128906248 +1.1345 0.01318359375 0.231597900390625 0.942932128906248 +1.134625 0.01318359375 0.231597900390625 0.942932128906248 +1.13475 0.013458251953125 0.236236572265625 0.942932128906248 +1.134875 0.013702392578125 0.2408447265625 0.942932128906248 +1.135 0.013702392578125 0.2408447265625 0.942932128906248 +1.135125 0.01397705078125 0.24542236328125 0.942932128906248 +1.13525 0.01397705078125 0.24542236328125 0.942932128906248 +1.135375 0.01422119140625 0.249969482421875 0.942932128906248 +1.1355 0.014495849609375 0.25445556640625 0.942932128906248 +1.135625 0.014495849609375 0.25445556640625 0.942932128906248 +1.13575 0.014739990234375 0.25897216796875 0.942932128906248 +1.135875 0.014739990234375 0.25897216796875 0.942932128906248 +1.1360000000000002 0.014984130859375 0.263427734375 0.942932128906248 +1.136125 0.0152587890625 0.267852783203125 0.942932128906248 +1.13625 0.0152587890625 0.267852783203125 0.942932128906248 +1.136375 0.0155029296875 0.27227783203125 0.942932128906248 +1.1365 0.0155029296875 0.27227783203125 0.942932128906248 +1.136625 0.0157470703125 0.276641845703125 0.942932128906248 +1.13675 0.0159912109375 0.280975341796875 0.942932128906248 +1.136875 0.0159912109375 0.280975341796875 0.942932128906248 +1.137 0.0162353515625 0.285308837890625 0.942932128906248 +1.137125 0.0162353515625 0.285308837890625 0.942932128906248 +1.13725 0.0164794921875 0.289581298828125 0.942932128906248 +1.137375 0.0167236328125 0.2938232421875 0.942932128906248 +1.1375 0.0167236328125 0.2938232421875 0.942932128906248 +1.137625 0.0169677734375 0.298065185546875 0.942932128906248 +1.13775 0.0169677734375 0.298065185546875 0.942932128906248 +1.137875 0.0172119140625 0.30224609375 0.942932128906248 +1.1380000000000002 0.0174560546875 0.306396484375 0.942932128906248 +1.138125 0.0174560546875 0.306396484375 0.942932128906248 +1.13825 0.017669677734375 0.310516357421875 0.942932128906248 +1.138375 0.017669677734375 0.310516357421875 0.942932128906248 +1.1385 0.017913818359375 0.314605712890625 0.942932128906248 +1.138625 0.018157958984375 0.31866455078125 0.942932128906248 +1.13875 0.018157958984375 0.31866455078125 0.942932128906248 +1.138875 0.01837158203125 0.322662353515625 0.942932128906248 +1.139 0.01837158203125 0.322662353515625 0.942932128906248 +1.139125 0.018585205078125 0.32666015625 0.942932128906248 +1.13925 0.018829345703125 0.330596923828125 0.942932128906248 +1.139375 0.018829345703125 0.330596923828125 0.942932128906248 +1.1395 0.01904296875 0.334503173828125 0.942932128906248 +1.139625 0.01904296875 0.334503173828125 0.942932128906248 +1.13975 0.019256591796875 0.33837890625 0.942932128906248 +1.139875 0.01947021484375 0.34222412109375 0.942932128906248 +1.1400000000000002 0.01947021484375 0.34222412109375 0.942932128906248 +1.140125 0.01971435546875 0.34600830078125 0.942932128906248 +1.14025 0.01971435546875 0.34600830078125 0.942932128906248 +1.140375 0.019927978515625 0.349761962890625 0.942932128906248 +1.1405 0.0201416015625 0.353485107421875 0.942932128906248 +1.140625 0.0201416015625 0.353485107421875 0.942932128906248 +1.14075 0.02032470703125 0.357177734375 0.942932128906248 +1.140875 0.02032470703125 0.357177734375 0.942932128906248 +1.141 0.020538330078125 0.360809326171875 0.942932128906248 +1.141125 0.020751953125 0.36444091796875 0.942932128906248 +1.14125 0.020751953125 0.36444091796875 0.942932128906248 +1.141375 0.020965576171875 0.36798095703125 0.942932128906248 +1.1415 0.020965576171875 0.36798095703125 0.942932128906248 +1.1416250000000002 0.021148681640625 0.37152099609375 0.942932128906248 +1.14175 0.0213623046875 0.375 0.942932128906248 +1.141875 0.0213623046875 0.375 0.942932128906248 +1.1420000000000002 0.02154541015625 0.378448486328125 0.942932128906248 +1.142125 0.02154541015625 0.378448486328125 0.942932128906248 +1.14225 0.021728515625 0.3818359375 0.942932128906248 +1.142375 0.021942138671875 0.38519287109375 0.942932128906248 +1.1425 0.021942138671875 0.38519287109375 0.942932128906248 +1.142625 0.022125244140625 0.388519287109375 0.942932128906248 +1.14275 0.022125244140625 0.388519287109375 0.942932128906248 +1.142875 0.022308349609375 0.39178466796875 0.942932128906248 +1.143 0.022491455078125 0.39501953125 0.942932128906248 +1.143125 0.022491455078125 0.39501953125 0.942932128906248 +1.14325 0.022674560546875 0.398193359375 0.942932128906248 +1.143375 0.022674560546875 0.398193359375 0.942932128906248 +1.1435 0.022857666015625 0.401336669921875 0.942932128906248 +1.1436250000000002 0.023040771484375 0.404449462890625 0.942932128906248 +1.14375 0.023040771484375 0.404449462890625 0.942932128906248 +1.143875 0.023193359375 0.407501220703125 0.942932128906248 +1.1440000000000002 0.023193359375 0.407501220703125 0.942932128906248 +1.144125 0.02337646484375 0.4105224609375 0.942932128906248 +1.14425 0.0235595703125 0.413482666015625 0.942932128906248 +1.144375 0.0235595703125 0.413482666015625 0.942932128906248 +1.1445 0.023712158203125 0.416412353515625 0.942932128906248 +1.144625 0.023712158203125 0.416412353515625 0.942932128906248 +1.14475 0.02386474609375 0.419281005859375 0.942932128906248 +1.144875 0.0240478515625 0.422119140625 0.942932128906248 +1.145 0.0240478515625 0.422119140625 0.942932128906248 +1.145125 0.024200439453125 0.424896240234375 0.942932128906248 +1.14525 0.024200439453125 0.424896240234375 0.942932128906248 +1.145375 0.02435302734375 0.4276123046875 0.942932128906248 +1.1455 0.024505615234375 0.4302978515625 0.942932128906248 +1.1456250000000002 0.024505615234375 0.4302978515625 0.942932128906248 +1.14575 0.024658203125 0.432952880859375 0.942932128906248 +1.145875 0.024658203125 0.432952880859375 0.942932128906248 +1.1460000000000002 0.024810791015625 0.435546875 0.942932128906248 +1.146125 0.024932861328125 0.438079833984375 0.942932128906248 +1.14625 0.024932861328125 0.438079833984375 0.942932128906248 +1.146375 0.02508544921875 0.440582275390625 0.942932128906248 +1.1465 0.02508544921875 0.440582275390625 0.942932128906248 +1.146625 0.025238037109375 0.44305419921875 0.942932128906248 +1.14675 0.025360107421875 0.4454345703125 0.942932128906248 +1.146875 0.025360107421875 0.4454345703125 0.942932128906248 +1.147 0.0255126953125 0.447784423828125 0.942932128906248 +1.147125 0.0255126953125 0.447784423828125 0.942932128906248 +1.14725 0.025634765625 0.450103759765625 0.942932128906248 +1.147375 0.0257568359375 0.452362060546875 0.942932128906248 +1.1475 0.0257568359375 0.452362060546875 0.942932128906248 +1.1476250000000002 0.02587890625 0.454559326171875 0.942932128906248 +1.14775 0.02587890625 0.454559326171875 0.942932128906248 +1.147875 0.0260009765625 0.45672607421875 0.942932128906248 +1.1480000000000002 0.026123046875 0.458831787109375 0.942932128906248 +1.148125 0.026123046875 0.458831787109375 0.942932128906248 +1.14825 0.0262451171875 0.46087646484375 0.942932128906248 +1.148375 0.0262451171875 0.46087646484375 0.942932128906248 +1.1485 0.0263671875 0.462890625 0.942932128906248 +1.148625 0.026458740234375 0.464813232421875 0.942932128906248 +1.14875 0.026458740234375 0.464813232421875 0.942932128906248 +1.148875 0.026580810546875 0.46673583984375 0.942932128906248 +1.149 0.026580810546875 0.46673583984375 0.942932128906248 +1.149125 0.02667236328125 0.46856689453125 0.942932128906248 +1.14925 0.02679443359375 0.470367431640625 0.942932128906248 +1.149375 0.02679443359375 0.470367431640625 0.942932128906248 +1.1495 0.026885986328125 0.472137451171875 0.942932128906248 +1.1496250000000002 0.026885986328125 0.472137451171875 0.942932128906248 +1.14975 0.0269775390625 0.47381591796875 0.942932128906248 +1.149875 0.027069091796875 0.4754638671875 0.942932128906248 +1.1500000000000002 0.027069091796875 0.4754638671875 0.942932128906248 +1.150125 0.02716064453125 0.47705078125 0.942932128906248 +1.15025 0.02716064453125 0.47705078125 0.942932128906248 +1.150375 0.027252197265625 0.478607177734375 0.942932128906248 +1.1505 0.02734375 0.480072021484375 0.942932128906248 +1.150625 0.02734375 0.480072021484375 0.942932128906248 +1.15075 0.027435302734375 0.48150634765625 0.942932128906248 +1.150875 0.027435302734375 0.48150634765625 0.942932128906248 +1.151 0.027496337890625 0.48291015625 0.942932128906248 +1.151125 0.027587890625 0.484222412109375 0.942932128906248 +1.15125 0.027587890625 0.484222412109375 0.942932128906248 +1.151375 0.02764892578125 0.485504150390625 0.942932128906248 +1.1515 0.02764892578125 0.485504150390625 0.942932128906248 +1.1516250000000002 0.0277099609375 0.486724853515625 0.942932128906248 +1.15175 0.02777099609375 0.487884521484375 0.942932128906248 +1.151875 0.02777099609375 0.487884521484375 0.942932128906248 +1.1520000000000002 0.128326416015625 0.489013671875 0.737463378906246 +1.152125 0.128326416015625 0.489013671875 0.737463378906246 +1.15225 0.12860107421875 0.490081787109375 0.737463378906246 +1.152375 0.128875732421875 0.4910888671875 0.737463378906246 +1.1525 0.128875732421875 0.4910888671875 0.737463378906246 +1.152625 0.129119873046875 0.492034912109375 0.737463378906246 +1.15275 0.129119873046875 0.492034912109375 0.737463378906246 +1.152875 0.129364013671875 0.492950439453125 0.737463378906246 +1.153 0.12957763671875 0.4937744140625 0.737463378906246 +1.153125 0.12957763671875 0.4937744140625 0.737463378906246 +1.15325 0.129791259765625 0.49456787109375 0.737463378906246 +1.153375 0.129791259765625 0.49456787109375 0.737463378906246 +1.1535 0.129974365234375 0.49530029296875 0.737463378906246 +1.1536250000000002 0.130157470703125 0.496002197265625 0.737463378906246 +1.15375 0.130157470703125 0.496002197265625 0.737463378906246 +1.153875 0.130340576171875 0.496612548828125 0.737463378906246 +1.154 0.130340576171875 0.496612548828125 0.737463378906246 +1.154125 0.1304931640625 0.4971923828125 0.737463378906246 +1.15425 0.130615234375 0.497711181640625 0.737463378906246 +1.154375 0.130615234375 0.497711181640625 0.737463378906246 +1.1545 0.1307373046875 0.498199462890625 0.737463378906246 +1.154625 0.1307373046875 0.498199462890625 0.737463378906246 +1.15475 0.130859375 0.49859619140625 0.737463378906246 +1.154875 0.130950927734375 0.49896240234375 0.737463378906246 +1.155 0.130950927734375 0.49896240234375 0.737463378906246 +1.155125 0.131011962890625 0.499267578125 0.737463378906246 +1.15525 0.131011962890625 0.499267578125 0.737463378906246 +1.155375 0.131103515625 0.49951171875 0.737463378906246 +1.1555 0.131134033203125 0.49969482421875 0.737463378906246 +1.1556250000000002 0.131134033203125 0.49969482421875 0.737463378906246 +1.15575 0.13116455078125 0.49981689453125 0.737463378906246 +1.155875 0.13116455078125 0.49981689453125 0.737463378906246 +1.156 0.131195068359375 0.499908447265625 0.737463378906246 +1.156125 0.131195068359375 0.49993896484375 0.737463378906246 +1.15625 0.131195068359375 0.49993896484375 0.737463378906246 +1.156375 0.131195068359375 0.499908447265625 0.737463378906246 +1.1565 0.131195068359375 0.499908447265625 0.737463378906246 +1.156625 0.13116455078125 0.49981689453125 0.737463378906246 +1.15675 0.131134033203125 0.49969482421875 0.737463378906246 +1.156875 0.131134033203125 0.49969482421875 0.737463378906246 +1.157 0.131103515625 0.49951171875 0.737463378906246 +1.157125 0.131103515625 0.49951171875 0.737463378906246 +1.1572500000000002 0.131011962890625 0.499267578125 0.737463378906246 +1.157375 0.130950927734375 0.49896240234375 0.737463378906246 +1.1575 0.130950927734375 0.49896240234375 0.737463378906246 +1.1576250000000002 0.130859375 0.49859619140625 0.737463378906246 +1.15775 0.130859375 0.49859619140625 0.737463378906246 +1.157875 0.1307373046875 0.498199462890625 0.737463378906246 +1.158 0.130615234375 0.497711181640625 0.737463378906246 +1.158125 0.130615234375 0.497711181640625 0.737463378906246 +1.15825 0.1304931640625 0.4971923828125 0.737463378906246 +1.158375 0.1304931640625 0.4971923828125 0.737463378906246 +1.1585 0.130340576171875 0.496612548828125 0.737463378906246 +1.158625 0.130157470703125 0.496002197265625 0.737463378906246 +1.15875 0.130157470703125 0.496002197265625 0.737463378906246 +1.158875 0.129974365234375 0.49530029296875 0.737463378906246 +1.159 0.129974365234375 0.49530029296875 0.737463378906246 +1.159125 0.129791259765625 0.49456787109375 0.737463378906246 +1.1592500000000002 0.12957763671875 0.4937744140625 0.737463378906246 +1.159375 0.12957763671875 0.4937744140625 0.737463378906246 +1.1595 0.129364013671875 0.492950439453125 0.737463378906246 +1.1596250000000002 0.129364013671875 0.492950439453125 0.737463378906246 +1.15975 0.129119873046875 0.492034912109375 0.737463378906246 +1.159875 0.128875732421875 0.4910888671875 0.737463378906246 +1.16 0.128875732421875 0.4910888671875 0.737463378906246 +1.160125 0.12860107421875 0.490081787109375 0.737463378906246 +1.16025 0.12860107421875 0.490081787109375 0.737463378906246 +1.160375 0.128326416015625 0.489013671875 0.737463378906246 +1.1605 0.1280517578125 0.487884521484375 0.737463378906246 +1.160625 0.1280517578125 0.487884521484375 0.737463378906246 +1.16075 0.12774658203125 0.486724853515625 0.737463378906246 +1.160875 0.12774658203125 0.486724853515625 0.737463378906246 +1.161 0.127410888671875 0.485504150390625 0.737463378906246 +1.161125 0.1270751953125 0.484222412109375 0.737463378906246 +1.1612500000000002 0.1270751953125 0.484222412109375 0.737463378906246 +1.161375 0.126739501953125 0.48291015625 0.737463378906246 +1.1615 0.126739501953125 0.48291015625 0.737463378906246 +1.1616250000000002 0.126373291015625 0.48150634765625 0.737463378906246 +1.16175 0.126007080078125 0.480072021484375 0.737463378906246 +1.161875 0.126007080078125 0.480072021484375 0.737463378906246 +1.162 0.1256103515625 0.478607177734375 0.737463378906246 +1.162125 0.1256103515625 0.478607177734375 0.737463378906246 +1.16225 0.125213623046875 0.47705078125 0.737463378906246 +1.162375 0.124786376953125 0.4754638671875 0.737463378906246 +1.1625 0.124786376953125 0.4754638671875 0.737463378906246 +1.162625 0.124359130859375 0.47381591796875 0.737463378906246 +1.16275 0.124359130859375 0.47381591796875 0.737463378906246 +1.162875 0.1239013671875 0.472137451171875 0.737463378906246 +1.163 0.123443603515625 0.470367431640625 0.737463378906246 +1.163125 0.123443603515625 0.470367431640625 0.737463378906246 +1.1632500000000002 0.12298583984375 0.46856689453125 0.737463378906246 +1.163375 0.12298583984375 0.46856689453125 0.737463378906246 +1.1635 0.12249755859375 0.46673583984375 0.737463378906246 +1.1636250000000002 0.121978759765625 0.464813232421875 0.737463378906246 +1.16375 0.121978759765625 0.464813232421875 0.737463378906246 +1.163875 0.121490478515625 0.462890625 0.737463378906246 +1.164 0.121490478515625 0.462890625 0.737463378906246 +1.164125 0.120941162109375 0.46087646484375 0.737463378906246 +1.16425 0.12042236328125 0.458831787109375 0.737463378906246 +1.164375 0.12042236328125 0.458831787109375 0.737463378906246 +1.1645 0.119873046875 0.45672607421875 0.737463378906246 +1.164625 0.119873046875 0.45672607421875 0.737463378906246 +1.16475 0.119293212890625 0.454559326171875 0.737463378906246 +1.164875 0.11871337890625 0.452362060546875 0.737463378906246 +1.165 0.11871337890625 0.452362060546875 0.737463378906246 +1.165125 0.118133544921875 0.450103759765625 0.737463378906246 +1.1652500000000002 0.118133544921875 0.450103759765625 0.737463378906246 +1.165375 0.117523193359375 0.447784423828125 0.737463378906246 +1.1655 0.116912841796875 0.4454345703125 0.737463378906246 +1.1656250000000002 0.116912841796875 0.4454345703125 0.737463378906246 +1.16575 0.11627197265625 0.44305419921875 0.737463378906246 +1.165875 0.11627197265625 0.44305419921875 0.737463378906246 +1.166 0.115631103515625 0.440582275390625 0.737463378906246 +1.166125 0.114959716796875 0.438079833984375 0.737463378906246 +1.16625 0.114959716796875 0.438079833984375 0.737463378906246 +1.166375 0.11431884765625 0.435546875 0.737463378906246 +1.1665 0.11431884765625 0.435546875 0.737463378906246 +1.166625 0.113616943359375 0.432952880859375 0.737463378906246 +1.16675 0.1129150390625 0.4302978515625 0.737463378906246 +1.166875 0.1129150390625 0.4302978515625 0.737463378906246 +1.167 0.112213134765625 0.4276123046875 0.737463378906246 +1.167125 0.112213134765625 0.4276123046875 0.737463378906246 +1.1672500000000002 0.11151123046875 0.424896240234375 0.737463378906246 +1.167375 0.11077880859375 0.422119140625 0.737463378906246 +1.1675 0.11077880859375 0.422119140625 0.737463378906246 +1.1676250000000002 0.11004638671875 0.419281005859375 0.737463378906246 +1.16775 0.11004638671875 0.419281005859375 0.737463378906246 +1.167875 0.109283447265625 0.416412353515625 0.737463378906246 +1.168 0.1085205078125 0.413482666015625 0.737463378906246 +1.168125 0.1085205078125 0.413482666015625 0.737463378906246 +1.16825 0.10772705078125 0.4105224609375 0.737463378906246 +1.168375 0.10772705078125 0.4105224609375 0.737463378906246 +1.1685 0.10693359375 0.407501220703125 0.737463378906246 +1.168625 0.10614013671875 0.404449462890625 0.737463378906246 +1.16875 0.10614013671875 0.404449462890625 0.737463378906246 +1.168875 0.105316162109375 0.401336669921875 0.737463378906246 +1.169 0.105316162109375 0.401336669921875 0.737463378906246 +1.169125 0.1044921875 0.398193359375 0.737463378906246 +1.1692500000000002 0.103668212890625 0.39501953125 0.737463378906246 +1.169375 0.103668212890625 0.39501953125 0.737463378906246 +1.1695 0.102813720703125 0.39178466796875 0.737463378906246 +1.169625 0.102813720703125 0.39178466796875 0.737463378906246 +1.16975 0.101959228515625 0.388519287109375 0.737463378906246 +1.169875 0.101104736328125 0.38519287109375 0.737463378906246 +1.17 0.101104736328125 0.38519287109375 0.737463378906246 +1.170125 0.1002197265625 0.3818359375 0.737463378906246 +1.17025 0.1002197265625 0.3818359375 0.737463378906246 +1.170375 0.09930419921875 0.378448486328125 0.737463378906246 +1.1705 0.098419189453125 0.375 0.737463378906246 +1.170625 0.098419189453125 0.375 0.737463378906246 +1.17075 0.097503662109375 0.37152099609375 0.737463378906246 +1.170875 0.097503662109375 0.37152099609375 0.737463378906246 +1.171 0.096588134765625 0.36798095703125 0.737463378906246 +1.171125 0.09564208984375 0.36444091796875 0.737463378906246 +1.1712500000000002 0.09564208984375 0.36444091796875 0.737463378906246 +1.171375 0.094696044921875 0.360809326171875 0.737463378906246 +1.1715 0.094696044921875 0.360809326171875 0.737463378906246 +1.171625 0.09375 0.357177734375 0.737463378906246 +1.17175 0.0927734375 0.353485107421875 0.737463378906246 +1.171875 0.0927734375 0.353485107421875 0.737463378906246 +1.172 0.091796875 0.349761962890625 0.737463378906246 +1.172125 0.091796875 0.349761962890625 0.737463378906246 +1.17225 0.0908203125 0.34600830078125 0.737463378906246 +1.172375 0.089813232421875 0.34222412109375 0.737463378906246 +1.1725 0.089813232421875 0.34222412109375 0.737463378906246 +1.172625 0.08880615234375 0.33837890625 0.737463378906246 +1.17275 0.08880615234375 0.33837890625 0.737463378906246 +1.1728750000000002 0.087799072265625 0.334503173828125 0.737463378906246 +1.173 0.086761474609375 0.330596923828125 0.737463378906246 +1.173125 0.086761474609375 0.330596923828125 0.737463378906246 +1.1732500000000002 0.085723876953125 0.32666015625 0.737463378906246 +1.173375 0.085723876953125 0.32666015625 0.737463378906246 +1.1735 0.084686279296875 0.322662353515625 0.737463378906246 +1.173625 0.0836181640625 0.31866455078125 0.737463378906246 +1.17375 0.0836181640625 0.31866455078125 0.737463378906246 +1.173875 0.082550048828125 0.314605712890625 0.737463378906246 +1.174 0.082550048828125 0.314605712890625 0.737463378906246 +1.174125 0.08148193359375 0.310516357421875 0.737463378906246 +1.17425 0.080413818359375 0.306396484375 0.737463378906246 +1.174375 0.080413818359375 0.306396484375 0.737463378906246 +1.1745 0.079315185546875 0.30224609375 0.737463378906246 +1.174625 0.079315185546875 0.30224609375 0.737463378906246 +1.17475 0.078216552734375 0.298065185546875 0.737463378906246 +1.1748750000000002 0.077117919921875 0.2938232421875 0.737463378906246 +1.175 0.077117919921875 0.2938232421875 0.737463378906246 +1.175125 0.07598876953125 0.289581298828125 0.737463378906246 +1.1752500000000002 0.07598876953125 0.289581298828125 0.737463378906246 +1.175375 0.074859619140625 0.285308837890625 0.737463378906246 +1.1755 0.07373046875 0.280975341796875 0.737463378906246 +1.175625 0.07373046875 0.280975341796875 0.737463378906246 +1.17575 0.072601318359375 0.276641845703125 0.737463378906246 +1.175875 0.072601318359375 0.276641845703125 0.737463378906246 +1.176 0.071441650390625 0.27227783203125 0.737463378906246 +1.176125 0.070281982421875 0.267852783203125 0.737463378906246 +1.17625 0.070281982421875 0.267852783203125 0.737463378906246 +1.176375 0.069122314453125 0.263427734375 0.737463378906246 +1.1765 0.069122314453125 0.263427734375 0.737463378906246 +1.176625 0.067962646484375 0.25897216796875 0.737463378906246 +1.17675 0.0667724609375 0.25445556640625 0.737463378906246 +1.1768750000000002 0.0667724609375 0.25445556640625 0.737463378906246 +1.177 0.065582275390625 0.249969482421875 0.737463378906246 +1.177125 0.065582275390625 0.249969482421875 0.737463378906246 +1.1772500000000002 0.06439208984375 0.24542236328125 0.737463378906246 +1.177375 0.063201904296875 0.2408447265625 0.737463378906246 +1.1775 0.063201904296875 0.2408447265625 0.737463378906246 +1.177625 0.061981201171875 0.236236572265625 0.737463378906246 +1.17775 0.061981201171875 0.236236572265625 0.737463378906246 +1.177875 0.060791015625 0.231597900390625 0.737463378906246 +1.178 0.059539794921875 0.226959228515625 0.737463378906246 +1.178125 0.059539794921875 0.226959228515625 0.737463378906246 +1.17825 0.058319091796875 0.2222900390625 0.737463378906246 +1.178375 0.058319091796875 0.2222900390625 0.737463378906246 +1.1785 0.057098388671875 0.21759033203125 0.737463378906246 +1.178625 0.05584716796875 0.212860107421875 0.737463378906246 +1.17875 0.05584716796875 0.212860107421875 0.737463378906246 +1.1788750000000002 0.054595947265625 0.208099365234375 0.737463378906246 +1.179 0.054595947265625 0.208099365234375 0.737463378906246 +1.179125 0.0533447265625 0.203338623046875 0.737463378906246 +1.1792500000000002 0.052093505859375 0.19854736328125 0.737463378906246 +1.179375 0.052093505859375 0.19854736328125 0.737463378906246 +1.1795 0.05084228515625 0.1937255859375 0.737463378906246 +1.179625 0.05084228515625 0.1937255859375 0.737463378906246 +1.17975 0.049560546875 0.188873291015625 0.737463378906246 +1.179875 0.04827880859375 0.18402099609375 0.737463378906246 +1.18 0.04827880859375 0.18402099609375 0.737463378906246 +1.180125 0.0469970703125 0.17913818359375 0.737463378906246 +1.18025 0.0469970703125 0.17913818359375 0.737463378906246 +1.180375 0.04571533203125 0.17425537109375 0.737463378906246 +1.1805 0.04443359375 0.169342041015625 0.737463378906246 +1.180625 0.04443359375 0.169342041015625 0.737463378906246 +1.18075 0.043121337890625 0.164398193359375 0.737463378906246 +1.1808750000000002 0.043121337890625 0.164398193359375 0.737463378906246 +1.181 0.041839599609375 0.159454345703125 0.737463378906246 +1.181125 0.04052734375 0.15447998046875 0.737463378906246 +1.1812500000000002 0.04052734375 0.15447998046875 0.737463378906246 +1.181375 0.039215087890625 0.14947509765625 0.737463378906246 +1.1815 0.039215087890625 0.14947509765625 0.737463378906246 +1.181625 0.03790283203125 0.14447021484375 0.737463378906246 +1.18175 0.036590576171875 0.13946533203125 0.737463378906246 +1.181875 0.036590576171875 0.13946533203125 0.737463378906246 +1.182 0.0352783203125 0.134429931640625 0.737463378906246 +1.182125 0.0352783203125 0.134429931640625 0.737463378906246 +1.18225 0.033935546875 0.129364013671875 0.737463378906246 +1.182375 0.032623291015625 0.124298095703125 0.737463378906246 +1.1825 0.032623291015625 0.124298095703125 0.737463378906246 +1.182625 0.031280517578125 0.119232177734375 0.737463378906246 +1.18275 0.031280517578125 0.119232177734375 0.737463378906246 +1.1828750000000002 0.029937744140625 0.1141357421875 0.737463378906246 +1.183 0.028594970703125 0.109039306640625 0.737463378906246 +1.183125 0.028594970703125 0.109039306640625 0.737463378906246 +1.1832500000000002 0.027252197265625 0.103912353515625 0.737463378906246 +1.183375 0.027252197265625 0.103912353515625 0.737463378906246 +1.1835 0.025909423828125 0.098785400390625 0.737463378906246 +1.183625 0.024566650390625 0.093658447265625 0.737463378906246 +1.18375 0.024566650390625 0.093658447265625 0.737463378906246 +1.183875 0.023223876953125 0.0885009765625 0.737463378906246 +1.184 0.05181884765625 0.0885009765625 0.41434936523436966 +1.184125 0.048797607421875 0.083343505859375 0.41434936523436966 +1.18425 0.0457763671875 0.07818603515625 0.41434936523436966 +1.184375 0.0457763671875 0.07818603515625 0.41434936523436966 +1.1845 0.042755126953125 0.072998046875 0.41434936523436966 +1.184625 0.042755126953125 0.072998046875 0.41434936523436966 +1.18475 0.039703369140625 0.06781005859375 0.41434936523436966 +1.1848750000000002 0.03668212890625 0.0626220703125 0.41434936523436966 +1.185 0.03668212890625 0.0626220703125 0.41434936523436966 +1.185125 0.03363037109375 0.05743408203125 0.41434936523436966 +1.18525 0.03363037109375 0.05743408203125 0.41434936523436966 +1.185375 0.03057861328125 0.05224609375 0.41434936523436966 +1.1855 0.02752685546875 0.047027587890625 0.41434936523436966 +1.185625 0.02752685546875 0.047027587890625 0.41434936523436966 +1.18575 0.02447509765625 0.04180908203125 0.41434936523436966 +1.185875 0.02447509765625 0.04180908203125 0.41434936523436966 +1.186 0.02142333984375 0.036590576171875 0.41434936523436966 +1.186125 0.01837158203125 0.0313720703125 0.41434936523436966 +1.18625 0.01837158203125 0.0313720703125 0.41434936523436966 +1.186375 0.015289306640625 0.026123046875 0.41434936523436966 +1.1865 0.015289306640625 0.026123046875 0.41434936523436966 +1.186625 0.012237548828125 0.020904541015625 0.41434936523436966 +1.18675 0.009185791015625 0.01568603515625 0.41434936523436966 +1.1868750000000002 0.009185791015625 0.01568603515625 0.41434936523436966 +1.187 0.006103515625 0.01043701171875 0.41434936523436966 +1.187125 0.006103515625 0.01043701171875 0.41434936523436966 +1.18725 0.0030517578125 0.005218505859375 0.41434936523436966 +1.187375 0.0 0.0 0.41434936523436966 +1.1875 0.0 0.0 0.41434936523436966 +1.187625 -0.0030517578125 -0.005218505859375 0.41434936523436966 +1.18775 -0.0030517578125 -0.005218505859375 0.41434936523436966 +1.187875 -0.006103515625 -0.01043701171875 0.41434936523436966 +1.188 -0.009185791015625 -0.01568603515625 0.41434936523436966 +1.188125 -0.009185791015625 -0.01568603515625 0.41434936523436966 +1.18825 -0.012237548828125 -0.020904541015625 0.41434936523436966 +1.188375 -0.012237548828125 -0.020904541015625 0.41434936523436966 +1.1885000000000002 -0.015289306640625 -0.026123046875 0.41434936523436966 +1.188625 -0.01837158203125 -0.0313720703125 0.41434936523436966 +1.18875 -0.01837158203125 -0.0313720703125 0.41434936523436966 +1.1888750000000002 -0.02142333984375 -0.036590576171875 0.41434936523436966 +1.189 -0.02142333984375 -0.036590576171875 0.41434936523436966 +1.189125 -0.02447509765625 -0.04180908203125 0.41434936523436966 +1.18925 -0.02752685546875 -0.047027587890625 0.41434936523436966 +1.189375 -0.02752685546875 -0.047027587890625 0.41434936523436966 +1.1895 -0.03057861328125 -0.05224609375 0.41434936523436966 +1.189625 -0.03057861328125 -0.05224609375 0.41434936523436966 +1.18975 -0.03363037109375 -0.05743408203125 0.41434936523436966 +1.189875 -0.03668212890625 -0.0626220703125 0.41434936523436966 +1.19 -0.03668212890625 -0.0626220703125 0.41434936523436966 +1.190125 -0.039703369140625 -0.06781005859375 0.41434936523436966 +1.19025 -0.039703369140625 -0.06781005859375 0.41434936523436966 +1.190375 -0.042755126953125 -0.072998046875 0.41434936523436966 +1.1905000000000002 -0.0457763671875 -0.07818603515625 0.41434936523436966 +1.190625 -0.0457763671875 -0.07818603515625 0.41434936523436966 +1.19075 -0.048797607421875 -0.083343505859375 0.41434936523436966 +1.1908750000000002 -0.048797607421875 -0.083343505859375 0.41434936523436966 +1.191 -0.05181884765625 -0.0885009765625 0.41434936523436966 +1.191125 -0.054840087890625 -0.093658447265625 0.41434936523436966 +1.19125 -0.054840087890625 -0.093658447265625 0.41434936523436966 +1.191375 -0.057861328125 -0.098785400390625 0.41434936523436966 +1.1915 -0.057861328125 -0.098785400390625 0.41434936523436966 +1.191625 -0.06085205078125 -0.103912353515625 0.41434936523436966 +1.19175 -0.0638427734375 -0.109039306640625 0.41434936523436966 +1.191875 -0.0638427734375 -0.109039306640625 0.41434936523436966 +1.192 -0.06683349609375 -0.1141357421875 0.41434936523436966 +1.192125 -0.06683349609375 -0.1141357421875 0.41434936523436966 +1.19225 -0.06982421875 -0.119232177734375 0.41434936523436966 +1.192375 -0.072784423828125 -0.124298095703125 0.41434936523436966 +1.1925000000000002 -0.072784423828125 -0.124298095703125 0.41434936523436966 +1.192625 -0.07574462890625 -0.129364013671875 0.41434936523436966 +1.19275 -0.07574462890625 -0.129364013671875 0.41434936523436966 +1.1928750000000002 -0.078704833984375 -0.134429931640625 0.41434936523436966 +1.193 -0.0816650390625 -0.13946533203125 0.41434936523436966 +1.193125 -0.0816650390625 -0.13946533203125 0.41434936523436966 +1.19325 -0.0845947265625 -0.14447021484375 0.41434936523436966 +1.193375 -0.0845947265625 -0.14447021484375 0.41434936523436966 +1.1935 -0.0875244140625 -0.14947509765625 0.41434936523436966 +1.193625 -0.0904541015625 -0.15447998046875 0.41434936523436966 +1.19375 -0.0904541015625 -0.15447998046875 0.41434936523436966 +1.193875 -0.093353271484375 -0.159454345703125 0.41434936523436966 +1.194 -0.093353271484375 -0.159454345703125 0.41434936523436966 +1.194125 -0.09625244140625 -0.164398193359375 0.41434936523436966 +1.19425 -0.099151611328125 -0.169342041015625 0.41434936523436966 +1.194375 -0.099151611328125 -0.169342041015625 0.41434936523436966 +1.1945000000000002 -0.102020263671875 -0.17425537109375 0.41434936523436966 +1.194625 -0.102020263671875 -0.17425537109375 0.41434936523436966 +1.19475 -0.104888916015625 -0.17913818359375 0.41434936523436966 +1.1948750000000002 -0.107757568359375 -0.18402099609375 0.41434936523436966 +1.195 -0.107757568359375 -0.18402099609375 0.41434936523436966 +1.195125 -0.110595703125 -0.188873291015625 0.41434936523436966 +1.19525 -0.110595703125 -0.188873291015625 0.41434936523436966 +1.195375 -0.113433837890625 -0.1937255859375 0.41434936523436966 +1.1955 -0.11627197265625 -0.19854736328125 0.41434936523436966 +1.195625 -0.11627197265625 -0.19854736328125 0.41434936523436966 +1.19575 -0.119049072265625 -0.203338623046875 0.41434936523436966 +1.195875 -0.119049072265625 -0.203338623046875 0.41434936523436966 +1.196 -0.121856689453125 -0.208099365234375 0.41434936523436966 +1.196125 -0.1246337890625 -0.212860107421875 0.41434936523436966 +1.19625 -0.1246337890625 -0.212860107421875 0.41434936523436966 +1.196375 -0.127410888671875 -0.21759033203125 0.41434936523436966 +1.1965000000000002 -0.127410888671875 -0.21759033203125 0.41434936523436966 +1.196625 -0.130157470703125 -0.2222900390625 0.41434936523436966 +1.19675 -0.132904052734375 -0.226959228515625 0.41434936523436966 +1.1968750000000002 -0.132904052734375 -0.226959228515625 0.41434936523436966 +1.197 -0.1356201171875 -0.231597900390625 0.41434936523436966 +1.197125 -0.1356201171875 -0.231597900390625 0.41434936523436966 +1.19725 -0.138336181640625 -0.236236572265625 0.41434936523436966 +1.197375 -0.141021728515625 -0.2408447265625 0.41434936523436966 +1.1975 -0.141021728515625 -0.2408447265625 0.41434936523436966 +1.197625 -0.143707275390625 -0.24542236328125 0.41434936523436966 +1.19775 -0.143707275390625 -0.24542236328125 0.41434936523436966 +1.197875 -0.1463623046875 -0.249969482421875 0.41434936523436966 +1.198 -0.149017333984375 -0.25445556640625 0.41434936523436966 +1.198125 -0.149017333984375 -0.25445556640625 0.41434936523436966 +1.19825 -0.151641845703125 -0.25897216796875 0.41434936523436966 +1.198375 -0.151641845703125 -0.25897216796875 0.41434936523436966 +1.1985000000000002 -0.154266357421875 -0.263427734375 0.41434936523436966 +1.198625 -0.1568603515625 -0.267852783203125 0.41434936523436966 +1.19875 -0.1568603515625 -0.267852783203125 0.41434936523436966 +1.1988750000000002 -0.159454345703125 -0.27227783203125 0.41434936523436966 +1.199 -0.159454345703125 -0.27227783203125 0.41434936523436966 +1.199125 -0.1619873046875 -0.276641845703125 0.41434936523436966 +1.19925 -0.16455078125 -0.280975341796875 0.41434936523436966 +1.199375 -0.16455078125 -0.280975341796875 0.41434936523436966 +1.1995 -0.167083740234375 -0.285308837890625 0.41434936523436966 +1.199625 -0.167083740234375 -0.285308837890625 0.41434936523436966 +1.19975 -0.169586181640625 -0.289581298828125 0.41434936523436966 +1.199875 -0.17205810546875 -0.2938232421875 0.41434936523436966 +1.2 -0.17205810546875 -0.2938232421875 0.41434936523436966 +1.200125 -0.174530029296875 -0.298065185546875 0.41434936523436966 +1.20025 -0.174530029296875 -0.298065185546875 0.41434936523436966 +1.200375 -0.177001953125 -0.30224609375 0.41434936523436966 +1.2005000000000002 -0.179443359375 -0.306396484375 0.41434936523436966 +1.200625 -0.179443359375 -0.306396484375 0.41434936523436966 +1.20075 -0.181854248046875 -0.310516357421875 0.41434936523436966 +1.200875 -0.181854248046875 -0.310516357421875 0.41434936523436966 +1.201 -0.184234619140625 -0.314605712890625 0.41434936523436966 +1.201125 -0.186614990234375 -0.31866455078125 0.41434936523436966 +1.20125 -0.186614990234375 -0.31866455078125 0.41434936523436966 +1.201375 -0.18896484375 -0.322662353515625 0.41434936523436966 +1.2015 -0.18896484375 -0.322662353515625 0.41434936523436966 +1.201625 -0.1912841796875 -0.32666015625 0.41434936523436966 +1.20175 -0.193603515625 -0.330596923828125 0.41434936523436966 +1.201875 -0.193603515625 -0.330596923828125 0.41434936523436966 +1.202 -0.195892333984375 -0.334503173828125 0.41434936523436966 +1.202125 -0.195892333984375 -0.334503173828125 0.41434936523436966 +1.20225 -0.198150634765625 -0.33837890625 0.41434936523436966 +1.202375 -0.200408935546875 -0.34222412109375 0.41434936523436966 +1.2025000000000002 -0.200408935546875 -0.34222412109375 0.41434936523436966 +1.202625 -0.20263671875 -0.34600830078125 0.41434936523436966 +1.20275 -0.20263671875 -0.34600830078125 0.41434936523436966 +1.202875 -0.204833984375 -0.349761962890625 0.41434936523436966 +1.203 -0.207000732421875 -0.353485107421875 0.41434936523436966 +1.203125 -0.207000732421875 -0.353485107421875 0.41434936523436966 +1.20325 -0.20916748046875 -0.357177734375 0.41434936523436966 +1.203375 -0.20916748046875 -0.357177734375 0.41434936523436966 +1.2035 -0.2113037109375 -0.360809326171875 0.41434936523436966 +1.203625 -0.213409423828125 -0.36444091796875 0.41434936523436966 +1.20375 -0.213409423828125 -0.36444091796875 0.41434936523436966 +1.203875 -0.215484619140625 -0.36798095703125 0.41434936523436966 +1.204 -0.215484619140625 -0.36798095703125 0.41434936523436966 +1.2041250000000002 -0.217559814453125 -0.37152099609375 0.41434936523436966 +1.20425 -0.2196044921875 -0.375 0.41434936523436966 +1.204375 -0.2196044921875 -0.375 0.41434936523436966 +1.2045000000000002 -0.22161865234375 -0.378448486328125 0.41434936523436966 +1.204625 -0.22161865234375 -0.378448486328125 0.41434936523436966 +1.20475 -0.223602294921875 -0.3818359375 0.41434936523436966 +1.204875 -0.225555419921875 -0.38519287109375 0.41434936523436966 +1.205 -0.225555419921875 -0.38519287109375 0.41434936523436966 +1.205125 -0.227508544921875 -0.388519287109375 0.41434936523436966 +1.20525 -0.227508544921875 -0.388519287109375 0.41434936523436966 +1.205375 -0.22943115234375 -0.39178466796875 0.41434936523436966 +1.2055 -0.2313232421875 -0.39501953125 0.41434936523436966 +1.205625 -0.2313232421875 -0.39501953125 0.41434936523436966 +1.20575 -0.233184814453125 -0.398193359375 0.41434936523436966 +1.205875 -0.233184814453125 -0.398193359375 0.41434936523436966 +1.206 -0.235015869140625 -0.401336669921875 0.41434936523436966 +1.2061250000000002 -0.236846923828125 -0.404449462890625 0.41434936523436966 +1.20625 -0.236846923828125 -0.404449462890625 0.41434936523436966 +1.206375 -0.2386474609375 -0.407501220703125 0.41434936523436966 +1.2065000000000002 -0.2386474609375 -0.407501220703125 0.41434936523436966 +1.206625 -0.240386962890625 -0.4105224609375 0.41434936523436966 +1.20675 -0.24212646484375 -0.413482666015625 0.41434936523436966 +1.206875 -0.24212646484375 -0.413482666015625 0.41434936523436966 +1.207 -0.24383544921875 -0.416412353515625 0.41434936523436966 +1.207125 -0.24383544921875 -0.416412353515625 0.41434936523436966 +1.20725 -0.245513916015625 -0.419281005859375 0.41434936523436966 +1.207375 -0.2471923828125 -0.422119140625 0.41434936523436966 +1.2075 -0.2471923828125 -0.422119140625 0.41434936523436966 +1.207625 -0.248809814453125 -0.424896240234375 0.41434936523436966 +1.20775 -0.248809814453125 -0.424896240234375 0.41434936523436966 +1.207875 -0.250396728515625 -0.4276123046875 0.41434936523436966 +1.208 -0.251983642578125 -0.4302978515625 0.41434936523436966 +1.2081250000000002 -0.251983642578125 -0.4302978515625 0.41434936523436966 +1.20825 -0.2535400390625 -0.432952880859375 0.41434936523436966 +1.208375 -0.2535400390625 -0.432952880859375 0.41434936523436966 +1.2085000000000002 -0.25506591796875 -0.435546875 0.41434936523436966 +1.208625 -0.25653076171875 -0.438079833984375 0.41434936523436966 +1.20875 -0.25653076171875 -0.438079833984375 0.41434936523436966 +1.208875 -0.25799560546875 -0.440582275390625 0.41434936523436966 +1.209 -0.25799560546875 -0.440582275390625 0.41434936523436966 +1.209125 -0.259429931640625 -0.44305419921875 0.41434936523436966 +1.20925 -0.260833740234375 -0.4454345703125 0.41434936523436966 +1.209375 -0.260833740234375 -0.4454345703125 0.41434936523436966 +1.2095 -0.26220703125 -0.447784423828125 0.41434936523436966 +1.209625 -0.26220703125 -0.447784423828125 0.41434936523436966 +1.20975 -0.263580322265625 -0.450103759765625 0.41434936523436966 +1.209875 -0.264892578125 -0.452362060546875 0.41434936523436966 +1.21 -0.264892578125 -0.452362060546875 0.41434936523436966 +1.2101250000000002 -0.26617431640625 -0.454559326171875 0.41434936523436966 +1.21025 -0.26617431640625 -0.454559326171875 0.41434936523436966 +1.210375 -0.2674560546875 -0.45672607421875 0.41434936523436966 +1.2105000000000002 -0.2686767578125 -0.458831787109375 0.41434936523436966 +1.210625 -0.2686767578125 -0.458831787109375 0.41434936523436966 +1.21075 -0.269866943359375 -0.46087646484375 0.41434936523436966 +1.210875 -0.269866943359375 -0.46087646484375 0.41434936523436966 +1.211 -0.27105712890625 -0.462890625 0.41434936523436966 +1.211125 -0.272186279296875 -0.464813232421875 0.41434936523436966 +1.21125 -0.272186279296875 -0.464813232421875 0.41434936523436966 +1.211375 -0.2733154296875 -0.46673583984375 0.41434936523436966 +1.2115 -0.2733154296875 -0.46673583984375 0.41434936523436966 +1.211625 -0.274383544921875 -0.46856689453125 0.41434936523436966 +1.21175 -0.27545166015625 -0.470367431640625 0.41434936523436966 +1.211875 -0.27545166015625 -0.470367431640625 0.41434936523436966 +1.212 -0.276458740234375 -0.472137451171875 0.41434936523436966 +1.2121250000000002 -0.276458740234375 -0.472137451171875 0.41434936523436966 +1.21225 -0.2774658203125 -0.47381591796875 0.41434936523436966 +1.212375 -0.2784423828125 -0.4754638671875 0.41434936523436966 +1.2125000000000002 -0.2784423828125 -0.4754638671875 0.41434936523436966 +1.212625 -0.27935791015625 -0.47705078125 0.41434936523436966 +1.21275 -0.27935791015625 -0.47705078125 0.41434936523436966 +1.212875 -0.2802734375 -0.478607177734375 0.41434936523436966 +1.213 -0.2811279296875 -0.480072021484375 0.41434936523436966 +1.213125 -0.2811279296875 -0.480072021484375 0.41434936523436966 +1.21325 -0.281982421875 -0.48150634765625 0.41434936523436966 +1.213375 -0.281982421875 -0.48150634765625 0.41434936523436966 +1.2135 -0.28277587890625 -0.48291015625 0.41434936523436966 +1.213625 -0.2835693359375 -0.484222412109375 0.41434936523436966 +1.21375 -0.2835693359375 -0.484222412109375 0.41434936523436966 +1.213875 -0.2843017578125 -0.485504150390625 0.41434936523436966 +1.214 -0.2843017578125 -0.485504150390625 0.41434936523436966 +1.2141250000000002 -0.2850341796875 -0.486724853515625 0.41434936523436966 +1.21425 -0.28570556640625 -0.487884521484375 0.41434936523436966 +1.214375 -0.28570556640625 -0.487884521484375 0.41434936523436966 +1.2145000000000002 -0.286346435546875 -0.489013671875 0.41434936523436966 +1.214625 -0.286346435546875 -0.489013671875 0.41434936523436966 +1.21475 -0.2869873046875 -0.490081787109375 0.41434936523436966 +1.214875 -0.287567138671875 -0.4910888671875 0.41434936523436966 +1.215 -0.287567138671875 -0.4910888671875 0.41434936523436966 +1.215125 -0.28814697265625 -0.492034912109375 0.41434936523436966 +1.21525 -0.28814697265625 -0.492034912109375 0.41434936523436966 +1.215375 -0.288665771484375 -0.492950439453125 0.41434936523436966 +1.2155 -0.289154052734375 -0.4937744140625 0.41434936523436966 +1.215625 -0.289154052734375 -0.4937744140625 0.41434936523436966 +1.21575 -0.28961181640625 -0.49456787109375 0.41434936523436966 +1.215875 -0.28961181640625 -0.49456787109375 0.41434936523436966 +1.216 -0.48284912109375 -0.49530029296875 0.025122070312493812 +1.2161250000000002 -0.4835205078125 -0.496002197265625 0.025122070312493812 +1.21625 -0.4835205078125 -0.496002197265625 0.025122070312493812 +1.216375 -0.484130859375 -0.496612548828125 0.025122070312493812 +1.2165 -0.484130859375 -0.496612548828125 0.025122070312493812 +1.216625 -0.48468017578125 -0.4971923828125 0.025122070312493812 +1.21675 -0.485198974609375 -0.497711181640625 0.025122070312493812 +1.216875 -0.485198974609375 -0.497711181640625 0.025122070312493812 +1.217 -0.48565673828125 -0.498199462890625 0.025122070312493812 +1.217125 -0.48565673828125 -0.498199462890625 0.025122070312493812 +1.21725 -0.486053466796875 -0.49859619140625 0.025122070312493812 +1.217375 -0.48638916015625 -0.49896240234375 0.025122070312493812 +1.2175 -0.48638916015625 -0.49896240234375 0.025122070312493812 +1.217625 -0.4866943359375 -0.499267578125 0.025122070312493812 +1.21775 -0.4866943359375 -0.499267578125 0.025122070312493812 +1.217875 -0.4869384765625 -0.49951171875 0.025122070312493812 +1.218 -0.48712158203125 -0.49969482421875 0.025122070312493812 +1.2181250000000002 -0.48712158203125 -0.49969482421875 0.025122070312493812 +1.21825 -0.48724365234375 -0.49981689453125 0.025122070312493812 +1.218375 -0.48724365234375 -0.49981689453125 0.025122070312493812 +1.2185 -0.487335205078125 -0.499908447265625 0.025122070312493812 +1.218625 -0.48736572265625 -0.49993896484375 0.025122070312493812 +1.21875 -0.48736572265625 -0.49993896484375 0.025122070312493812 +1.218875 -0.487335205078125 -0.499908447265625 0.025122070312493812 +1.219 -0.487335205078125 -0.499908447265625 0.025122070312493812 +1.219125 -0.48724365234375 -0.49981689453125 0.025122070312493812 +1.21925 -0.48712158203125 -0.49969482421875 0.025122070312493812 +1.219375 -0.48712158203125 -0.49969482421875 0.025122070312493812 +1.2195 -0.4869384765625 -0.49951171875 0.025122070312493812 +1.219625 -0.4869384765625 -0.49951171875 0.025122070312493812 +1.2197500000000002 -0.4866943359375 -0.499267578125 0.025122070312493812 +1.219875 -0.48638916015625 -0.49896240234375 0.025122070312493812 +1.22 -0.48638916015625 -0.49896240234375 0.025122070312493812 +1.2201250000000002 -0.486053466796875 -0.49859619140625 0.025122070312493812 +1.22025 -0.486053466796875 -0.49859619140625 0.025122070312493812 +1.220375 -0.48565673828125 -0.498199462890625 0.025122070312493812 +1.2205 -0.485198974609375 -0.497711181640625 0.025122070312493812 +1.220625 -0.485198974609375 -0.497711181640625 0.025122070312493812 +1.22075 -0.48468017578125 -0.4971923828125 0.025122070312493812 +1.220875 -0.48468017578125 -0.4971923828125 0.025122070312493812 +1.221 -0.484130859375 -0.496612548828125 0.025122070312493812 +1.221125 -0.4835205078125 -0.496002197265625 0.025122070312493812 +1.22125 -0.4835205078125 -0.496002197265625 0.025122070312493812 +1.221375 -0.48284912109375 -0.49530029296875 0.025122070312493812 +1.2215 -0.48284912109375 -0.49530029296875 0.025122070312493812 +1.221625 -0.48211669921875 -0.49456787109375 0.025122070312493812 +1.2217500000000002 -0.481353759765625 -0.4937744140625 0.025122070312493812 +1.221875 -0.481353759765625 -0.4937744140625 0.025122070312493812 +1.222 -0.48052978515625 -0.492950439453125 0.025122070312493812 +1.2221250000000002 -0.48052978515625 -0.492950439453125 0.025122070312493812 +1.22225 -0.47967529296875 -0.492034912109375 0.025122070312493812 +1.222375 -0.478729248046875 -0.4910888671875 0.025122070312493812 +1.2225 -0.478729248046875 -0.4910888671875 0.025122070312493812 +1.222625 -0.477752685546875 -0.490081787109375 0.025122070312493812 +1.22275 -0.477752685546875 -0.490081787109375 0.025122070312493812 +1.222875 -0.476715087890625 -0.489013671875 0.025122070312493812 +1.223 -0.475616455078125 -0.487884521484375 0.025122070312493812 +1.223125 -0.475616455078125 -0.487884521484375 0.025122070312493812 +1.22325 -0.4744873046875 -0.486724853515625 0.025122070312493812 +1.223375 -0.4744873046875 -0.486724853515625 0.025122070312493812 +1.2235 -0.473297119140625 -0.485504150390625 0.025122070312493812 +1.223625 -0.4720458984375 -0.484222412109375 0.025122070312493812 +1.2237500000000002 -0.4720458984375 -0.484222412109375 0.025122070312493812 +1.223875 -0.47076416015625 -0.48291015625 0.025122070312493812 +1.224 -0.47076416015625 -0.48291015625 0.025122070312493812 +1.2241250000000002 -0.469390869140625 -0.48150634765625 0.025122070312493812 +1.22425 -0.467987060546875 -0.480072021484375 0.025122070312493812 +1.224375 -0.467987060546875 -0.480072021484375 0.025122070312493812 +1.2245 -0.466552734375 -0.478607177734375 0.025122070312493812 +1.224625 -0.466552734375 -0.478607177734375 0.025122070312493812 +1.22475 -0.465057373046875 -0.47705078125 0.025122070312493812 +1.224875 -0.4635009765625 -0.4754638671875 0.025122070312493812 +1.225 -0.4635009765625 -0.4754638671875 0.025122070312493812 +1.225125 -0.4619140625 -0.47381591796875 0.025122070312493812 +1.22525 -0.4619140625 -0.47381591796875 0.025122070312493812 +1.225375 -0.460235595703125 -0.472137451171875 0.025122070312493812 +1.2255 -0.458526611328125 -0.470367431640625 0.025122070312493812 +1.225625 -0.458526611328125 -0.470367431640625 0.025122070312493812 +1.2257500000000002 -0.456787109375 -0.46856689453125 0.025122070312493812 +1.225875 -0.456787109375 -0.46856689453125 0.025122070312493812 +1.226 -0.454986572265625 -0.46673583984375 0.025122070312493812 +1.2261250000000002 -0.453125 -0.464813232421875 0.025122070312493812 +1.22625 -0.453125 -0.464813232421875 0.025122070312493812 +1.226375 -0.45123291015625 -0.462890625 0.025122070312493812 +1.2265 -0.45123291015625 -0.462890625 0.025122070312493812 +1.226625 -0.44927978515625 -0.46087646484375 0.025122070312493812 +1.22675 -0.447265625 -0.458831787109375 0.025122070312493812 +1.226875 -0.447265625 -0.458831787109375 0.025122070312493812 +1.227 -0.445220947265625 -0.45672607421875 0.025122070312493812 +1.227125 -0.445220947265625 -0.45672607421875 0.025122070312493812 +1.22725 -0.443115234375 -0.454559326171875 0.025122070312493812 +1.227375 -0.44097900390625 -0.452362060546875 0.025122070312493812 +1.2275 -0.44097900390625 -0.452362060546875 0.025122070312493812 +1.227625 -0.43878173828125 -0.450103759765625 0.025122070312493812 +1.2277500000000002 -0.43878173828125 -0.450103759765625 0.025122070312493812 +1.227875 -0.4365234375 -0.447784423828125 0.025122070312493812 +1.228 -0.434234619140625 -0.4454345703125 0.025122070312493812 +1.2281250000000002 -0.434234619140625 -0.4454345703125 0.025122070312493812 +1.22825 -0.431915283203125 -0.44305419921875 0.025122070312493812 +1.228375 -0.431915283203125 -0.44305419921875 0.025122070312493812 +1.2285 -0.42950439453125 -0.440582275390625 0.025122070312493812 +1.228625 -0.42706298828125 -0.438079833984375 0.025122070312493812 +1.22875 -0.42706298828125 -0.438079833984375 0.025122070312493812 +1.228875 -0.424591064453125 -0.435546875 0.025122070312493812 +1.229 -0.424591064453125 -0.435546875 0.025122070312493812 +1.229125 -0.42205810546875 -0.432952880859375 0.025122070312493812 +1.22925 -0.41949462890625 -0.4302978515625 0.025122070312493812 +1.229375 -0.41949462890625 -0.4302978515625 0.025122070312493812 +1.2295 -0.4168701171875 -0.4276123046875 0.025122070312493812 +1.229625 -0.4168701171875 -0.4276123046875 0.025122070312493812 +1.2297500000000002 -0.414215087890625 -0.424896240234375 0.025122070312493812 +1.229875 -0.4114990234375 -0.422119140625 0.025122070312493812 +1.23 -0.4114990234375 -0.422119140625 0.025122070312493812 +1.2301250000000002 -0.408721923828125 -0.419281005859375 0.025122070312493812 +1.23025 -0.408721923828125 -0.419281005859375 0.025122070312493812 +1.230375 -0.40594482421875 -0.416412353515625 0.025122070312493812 +1.2305 -0.403076171875 -0.413482666015625 0.025122070312493812 +1.230625 -0.403076171875 -0.413482666015625 0.025122070312493812 +1.23075 -0.400177001953125 -0.4105224609375 0.025122070312493812 +1.230875 -0.400177001953125 -0.4105224609375 0.025122070312493812 +1.231 -0.397247314453125 -0.407501220703125 0.025122070312493812 +1.231125 -0.394287109375 -0.404449462890625 0.025122070312493812 +1.23125 -0.394287109375 -0.404449462890625 0.025122070312493812 +1.231375 -0.3912353515625 -0.401336669921875 0.025122070312493812 +1.2315 -0.3912353515625 -0.401336669921875 0.025122070312493812 +1.231625 -0.38818359375 -0.398193359375 0.025122070312493812 +1.2317500000000002 -0.385101318359375 -0.39501953125 0.025122070312493812 +1.231875 -0.385101318359375 -0.39501953125 0.025122070312493812 +1.232 -0.381927490234375 -0.39178466796875 0.025122070312493812 +1.232125 -0.381927490234375 -0.39178466796875 0.025122070312493812 +1.23225 -0.378753662109375 -0.388519287109375 0.025122070312493812 +1.232375 -0.375518798828125 -0.38519287109375 0.025122070312493812 +1.2325 -0.375518798828125 -0.38519287109375 0.025122070312493812 +1.232625 -0.37225341796875 -0.3818359375 0.025122070312493812 +1.23275 -0.37225341796875 -0.3818359375 0.025122070312493812 +1.232875 -0.368927001953125 -0.378448486328125 0.025122070312493812 +1.233 -0.365570068359375 -0.375 0.025122070312493812 +1.233125 -0.365570068359375 -0.375 0.025122070312493812 +1.23325 -0.3621826171875 -0.37152099609375 0.025122070312493812 +1.233375 -0.3621826171875 -0.37152099609375 0.025122070312493812 +1.2335 -0.358734130859375 -0.36798095703125 0.025122070312493812 +1.233625 -0.355255126953125 -0.36444091796875 0.025122070312493812 +1.2337500000000002 -0.355255126953125 -0.36444091796875 0.025122070312493812 +1.233875 -0.35174560546875 -0.360809326171875 0.025122070312493812 +1.234 -0.35174560546875 -0.360809326171875 0.025122070312493812 +1.234125 -0.34820556640625 -0.357177734375 0.025122070312493812 +1.23425 -0.3446044921875 -0.353485107421875 0.025122070312493812 +1.234375 -0.3446044921875 -0.353485107421875 0.025122070312493812 +1.2345 -0.340972900390625 -0.349761962890625 0.025122070312493812 +1.234625 -0.340972900390625 -0.349761962890625 0.025122070312493812 +1.23475 -0.337310791015625 -0.34600830078125 0.025122070312493812 +1.234875 -0.3336181640625 -0.34222412109375 0.025122070312493812 +1.235 -0.3336181640625 -0.34222412109375 0.025122070312493812 +1.235125 -0.329864501953125 -0.33837890625 0.025122070312493812 +1.23525 -0.329864501953125 -0.33837890625 0.025122070312493812 +1.2353750000000002 -0.32611083984375 -0.334503173828125 0.025122070312493812 +1.2355 -0.322296142578125 -0.330596923828125 0.025122070312493812 +1.235625 -0.322296142578125 -0.330596923828125 0.025122070312493812 +1.2357500000000002 -0.318450927734375 -0.32666015625 0.025122070312493812 +1.235875 -0.318450927734375 -0.32666015625 0.025122070312493812 +1.236 -0.314544677734375 -0.322662353515625 0.025122070312493812 +1.236125 -0.310638427734375 -0.31866455078125 0.025122070312493812 +1.23625 -0.310638427734375 -0.31866455078125 0.025122070312493812 +1.236375 -0.30670166015625 -0.314605712890625 0.025122070312493812 +1.2365 -0.30670166015625 -0.314605712890625 0.025122070312493812 +1.236625 -0.302703857421875 -0.310516357421875 0.025122070312493812 +1.23675 -0.2987060546875 -0.306396484375 0.025122070312493812 +1.236875 -0.2987060546875 -0.306396484375 0.025122070312493812 +1.237 -0.294647216796875 -0.30224609375 0.025122070312493812 +1.237125 -0.294647216796875 -0.30224609375 0.025122070312493812 +1.23725 -0.290557861328125 -0.298065185546875 0.025122070312493812 +1.2373750000000002 -0.28643798828125 -0.2938232421875 0.025122070312493812 +1.2375 -0.28643798828125 -0.2938232421875 0.025122070312493812 +1.237625 -0.282318115234375 -0.289581298828125 0.025122070312493812 +1.2377500000000002 -0.282318115234375 -0.289581298828125 0.025122070312493812 +1.237875 -0.27813720703125 -0.285308837890625 0.025122070312493812 +1.238 -0.27392578125 -0.280975341796875 0.025122070312493812 +1.238125 -0.27392578125 -0.280975341796875 0.025122070312493812 +1.23825 -0.269683837890625 -0.276641845703125 0.025122070312493812 +1.238375 -0.269683837890625 -0.276641845703125 0.025122070312493812 +1.2385 -0.26544189453125 -0.27227783203125 0.025122070312493812 +1.238625 -0.261138916015625 -0.267852783203125 0.025122070312493812 +1.23875 -0.261138916015625 -0.267852783203125 0.025122070312493812 +1.238875 -0.256805419921875 -0.263427734375 0.025122070312493812 +1.239 -0.256805419921875 -0.263427734375 0.025122070312493812 +1.239125 -0.252471923828125 -0.25897216796875 0.025122070312493812 +1.23925 -0.248077392578125 -0.25445556640625 0.025122070312493812 +1.2393750000000002 -0.248077392578125 -0.25445556640625 0.025122070312493812 +1.2395 -0.243682861328125 -0.249969482421875 0.025122070312493812 +1.239625 -0.243682861328125 -0.249969482421875 0.025122070312493812 +1.2397500000000002 -0.239227294921875 -0.24542236328125 0.025122070312493812 +1.239875 -0.234771728515625 -0.2408447265625 0.025122070312493812 +1.24 -0.234771728515625 -0.2408447265625 0.025122070312493812 +1.240125 -0.23028564453125 -0.236236572265625 0.025122070312493812 +1.24025 -0.23028564453125 -0.236236572265625 0.025122070312493812 +1.240375 -0.22576904296875 -0.231597900390625 0.025122070312493812 +1.2405 -0.22125244140625 -0.226959228515625 0.025122070312493812 +1.240625 -0.22125244140625 -0.226959228515625 0.025122070312493812 +1.24075 -0.2166748046875 -0.2222900390625 0.025122070312493812 +1.240875 -0.2166748046875 -0.2222900390625 0.025122070312493812 +1.241 -0.21209716796875 -0.21759033203125 0.025122070312493812 +1.241125 -0.207489013671875 -0.212860107421875 0.025122070312493812 +1.24125 -0.207489013671875 -0.212860107421875 0.025122070312493812 +1.2413750000000002 -0.202880859375 -0.208099365234375 0.025122070312493812 +1.2415 -0.202880859375 -0.208099365234375 0.025122070312493812 +1.241625 -0.198211669921875 -0.203338623046875 0.025122070312493812 +1.2417500000000002 -0.19354248046875 -0.19854736328125 0.025122070312493812 +1.241875 -0.19354248046875 -0.19854736328125 0.025122070312493812 +1.242 -0.1888427734375 -0.1937255859375 0.025122070312493812 +1.242125 -0.1888427734375 -0.1937255859375 0.025122070312493812 +1.24225 -0.184112548828125 -0.188873291015625 0.025122070312493812 +1.242375 -0.17938232421875 -0.18402099609375 0.025122070312493812 +1.2425 -0.17938232421875 -0.18402099609375 0.025122070312493812 +1.242625 -0.174652099609375 -0.17913818359375 0.025122070312493812 +1.24275 -0.174652099609375 -0.17913818359375 0.025122070312493812 +1.242875 -0.16986083984375 -0.17425537109375 0.025122070312493812 +1.243 -0.165069580078125 -0.169342041015625 0.025122070312493812 +1.243125 -0.165069580078125 -0.169342041015625 0.025122070312493812 +1.24325 -0.160247802734375 -0.164398193359375 0.025122070312493812 +1.2433750000000002 -0.160247802734375 -0.164398193359375 0.025122070312493812 +1.2435 -0.155426025390625 -0.159454345703125 0.025122070312493812 +1.243625 -0.15057373046875 -0.15447998046875 0.025122070312493812 +1.2437500000000002 -0.15057373046875 -0.15447998046875 0.025122070312493812 +1.243875 -0.145721435546875 -0.14947509765625 0.025122070312493812 +1.244 -0.145721435546875 -0.14947509765625 0.025122070312493812 +1.244125 -0.140838623046875 -0.14447021484375 0.025122070312493812 +1.24425 -0.135955810546875 -0.13946533203125 0.025122070312493812 +1.244375 -0.135955810546875 -0.13946533203125 0.025122070312493812 +1.2445 -0.13104248046875 -0.134429931640625 0.025122070312493812 +1.244625 -0.13104248046875 -0.134429931640625 0.025122070312493812 +1.24475 -0.126129150390625 -0.129364013671875 0.025122070312493812 +1.244875 -0.121185302734375 -0.124298095703125 0.025122070312493812 +1.245 -0.121185302734375 -0.124298095703125 0.025122070312493812 +1.245125 -0.116241455078125 -0.119232177734375 0.025122070312493812 +1.24525 -0.116241455078125 -0.119232177734375 0.025122070312493812 +1.2453750000000002 -0.11126708984375 -0.1141357421875 0.025122070312493812 +1.2455 -0.106292724609375 -0.109039306640625 0.025122070312493812 +1.245625 -0.106292724609375 -0.109039306640625 0.025122070312493812 +1.2457500000000002 -0.101318359375 -0.103912353515625 0.025122070312493812 +1.245875 -0.101318359375 -0.103912353515625 0.025122070312493812 +1.246 -0.0963134765625 -0.098785400390625 0.025122070312493812 +1.246125 -0.09130859375 -0.093658447265625 0.025122070312493812 +1.24625 -0.09130859375 -0.093658447265625 0.025122070312493812 +1.246375 -0.086273193359375 -0.0885009765625 0.025122070312493812 +1.2465 -0.086273193359375 -0.0885009765625 0.025122070312493812 +1.246625 -0.081268310546875 -0.083343505859375 0.025122070312493812 +1.24675 -0.076202392578125 -0.07818603515625 0.025122070312493812 +1.246875 -0.076202392578125 -0.07818603515625 0.025122070312493812 +1.247 -0.0711669921875 -0.072998046875 0.025122070312493812 +1.247125 -0.0711669921875 -0.072998046875 0.025122070312493812 +1.24725 -0.06610107421875 -0.06781005859375 0.025122070312493812 +1.2473750000000002 -0.061065673828125 -0.0626220703125 0.025122070312493812 +1.2475 -0.061065673828125 -0.0626220703125 0.025122070312493812 +1.247625 -0.055999755859375 -0.05743408203125 0.025122070312493812 +1.2477500000000002 -0.055999755859375 -0.05743408203125 0.025122070312493812 +1.247875 -0.050933837890625 -0.05224609375 0.025122070312493812 +1.248 -0.047027587890625 -0.029693603515625 -0.3681030273437555 +1.248125 -0.047027587890625 -0.029693603515625 -0.3681030273437555 +1.24825 -0.04180908203125 -0.026397705078125 -0.3681030273437555 +1.248375 -0.04180908203125 -0.026397705078125 -0.3681030273437555 +1.2485 -0.036590576171875 -0.023101806640625 -0.3681030273437555 +1.248625 -0.0313720703125 -0.019805908203125 -0.3681030273437555 +1.24875 -0.0313720703125 -0.019805908203125 -0.3681030273437555 +1.248875 -0.026123046875 -0.016510009765625 -0.3681030273437555 +1.249 -0.026123046875 -0.016510009765625 -0.3681030273437555 +1.249125 -0.020904541015625 -0.013214111328125 -0.3681030273437555 +1.24925 -0.01568603515625 -0.009918212890625 -0.3681030273437555 +1.2493750000000002 -0.01568603515625 -0.009918212890625 -0.3681030273437555 +1.2495 -0.01043701171875 -0.006591796875 -0.3681030273437555 +1.249625 -0.01043701171875 -0.006591796875 -0.3681030273437555 +1.24975 -0.005218505859375 -0.0032958984375 -0.3681030273437555 +1.249875 0.0 0.0 -0.3681030273437555 +1.25 0.0 0.0 -0.3681030273437555 +1.250125 0.005218505859375 0.0032958984375 -0.3681030273437555 +1.25025 0.005218505859375 0.0032958984375 -0.3681030273437555 +1.250375 0.01043701171875 0.006591796875 -0.3681030273437555 +1.2505 0.01568603515625 0.009918212890625 -0.3681030273437555 +1.250625 0.01568603515625 0.009918212890625 -0.3681030273437555 +1.25075 0.020904541015625 0.013214111328125 -0.3681030273437555 +1.250875 0.020904541015625 0.013214111328125 -0.3681030273437555 +1.2510000000000002 0.026123046875 0.016510009765625 -0.3681030273437555 +1.251125 0.0313720703125 0.019805908203125 -0.3681030273437555 +1.25125 0.0313720703125 0.019805908203125 -0.3681030273437555 +1.2513750000000002 0.036590576171875 0.023101806640625 -0.3681030273437555 +1.2515 0.036590576171875 0.023101806640625 -0.3681030273437555 +1.251625 0.04180908203125 0.026397705078125 -0.3681030273437555 +1.25175 0.047027587890625 0.029693603515625 -0.3681030273437555 +1.251875 0.047027587890625 0.029693603515625 -0.3681030273437555 +1.252 0.05224609375 0.032989501953125 -0.3681030273437555 +1.252125 0.05224609375 0.032989501953125 -0.3681030273437555 +1.25225 0.05743408203125 0.036285400390625 -0.3681030273437555 +1.252375 0.0626220703125 0.039581298828125 -0.3681030273437555 +1.2525 0.0626220703125 0.039581298828125 -0.3681030273437555 +1.252625 0.06781005859375 0.0428466796875 -0.3681030273437555 +1.25275 0.06781005859375 0.0428466796875 -0.3681030273437555 +1.252875 0.072998046875 0.046112060546875 -0.3681030273437555 +1.2530000000000002 0.07818603515625 0.049407958984375 -0.3681030273437555 +1.253125 0.07818603515625 0.049407958984375 -0.3681030273437555 +1.25325 0.083343505859375 0.05267333984375 -0.3681030273437555 +1.2533750000000002 0.083343505859375 0.05267333984375 -0.3681030273437555 +1.2535 0.0885009765625 0.055908203125 -0.3681030273437555 +1.253625 0.093658447265625 0.059173583984375 -0.3681030273437555 +1.25375 0.093658447265625 0.059173583984375 -0.3681030273437555 +1.253875 0.098785400390625 0.062408447265625 -0.3681030273437555 +1.254 0.098785400390625 0.062408447265625 -0.3681030273437555 +1.254125 0.103912353515625 0.065643310546875 -0.3681030273437555 +1.25425 0.109039306640625 0.068878173828125 -0.3681030273437555 +1.254375 0.109039306640625 0.068878173828125 -0.3681030273437555 +1.2545 0.1141357421875 0.072113037109375 -0.3681030273437555 +1.254625 0.1141357421875 0.072113037109375 -0.3681030273437555 +1.25475 0.119232177734375 0.075347900390625 -0.3681030273437555 +1.254875 0.124298095703125 0.078521728515625 -0.3681030273437555 +1.2550000000000002 0.124298095703125 0.078521728515625 -0.3681030273437555 +1.255125 0.129364013671875 0.08172607421875 -0.3681030273437555 +1.25525 0.129364013671875 0.08172607421875 -0.3681030273437555 +1.2553750000000002 0.134429931640625 0.084930419921875 -0.3681030273437555 +1.2555 0.13946533203125 0.088104248046875 -0.3681030273437555 +1.255625 0.13946533203125 0.088104248046875 -0.3681030273437555 +1.25575 0.14447021484375 0.091278076171875 -0.3681030273437555 +1.255875 0.14447021484375 0.091278076171875 -0.3681030273437555 +1.256 0.14947509765625 0.094451904296875 -0.3681030273437555 +1.256125 0.15447998046875 0.09759521484375 -0.3681030273437555 +1.25625 0.15447998046875 0.09759521484375 -0.3681030273437555 +1.256375 0.159454345703125 0.100738525390625 -0.3681030273437555 +1.2565 0.159454345703125 0.100738525390625 -0.3681030273437555 +1.256625 0.164398193359375 0.103851318359375 -0.3681030273437555 +1.25675 0.169342041015625 0.10699462890625 -0.3681030273437555 +1.256875 0.169342041015625 0.10699462890625 -0.3681030273437555 +1.2570000000000002 0.17425537109375 0.110076904296875 -0.3681030273437555 +1.257125 0.17425537109375 0.110076904296875 -0.3681030273437555 +1.25725 0.17913818359375 0.113189697265625 -0.3681030273437555 +1.2573750000000002 0.18402099609375 0.11627197265625 -0.3681030273437555 +1.2575 0.18402099609375 0.11627197265625 -0.3681030273437555 +1.257625 0.188873291015625 0.11932373046875 -0.3681030273437555 +1.25775 0.188873291015625 0.11932373046875 -0.3681030273437555 +1.257875 0.1937255859375 0.122406005859375 -0.3681030273437555 +1.258 0.19854736328125 0.12542724609375 -0.3681030273437555 +1.258125 0.19854736328125 0.12542724609375 -0.3681030273437555 +1.25825 0.203338623046875 0.12847900390625 -0.3681030273437555 +1.258375 0.203338623046875 0.12847900390625 -0.3681030273437555 +1.2585 0.208099365234375 0.1314697265625 -0.3681030273437555 +1.258625 0.212860107421875 0.134490966796875 -0.3681030273437555 +1.25875 0.212860107421875 0.134490966796875 -0.3681030273437555 +1.258875 0.21759033203125 0.137481689453125 -0.3681030273437555 +1.2590000000000002 0.21759033203125 0.137481689453125 -0.3681030273437555 +1.259125 0.2222900390625 0.14044189453125 -0.3681030273437555 +1.25925 0.226959228515625 0.143402099609375 -0.3681030273437555 +1.2593750000000002 0.226959228515625 0.143402099609375 -0.3681030273437555 +1.2595 0.231597900390625 0.146331787109375 -0.3681030273437555 +1.259625 0.231597900390625 0.146331787109375 -0.3681030273437555 +1.25975 0.236236572265625 0.149261474609375 -0.3681030273437555 +1.259875 0.2408447265625 0.15216064453125 -0.3681030273437555 +1.26 0.2408447265625 0.15216064453125 -0.3681030273437555 +1.260125 0.24542236328125 0.155059814453125 -0.3681030273437555 +1.26025 0.24542236328125 0.155059814453125 -0.3681030273437555 +1.260375 0.249969482421875 0.157928466796875 -0.3681030273437555 +1.2605 0.25445556640625 0.1607666015625 -0.3681030273437555 +1.260625 0.25445556640625 0.1607666015625 -0.3681030273437555 +1.26075 0.25897216796875 0.16363525390625 -0.3681030273437555 +1.260875 0.25897216796875 0.16363525390625 -0.3681030273437555 +1.2610000000000002 0.263427734375 0.16644287109375 -0.3681030273437555 +1.261125 0.267852783203125 0.16925048828125 -0.3681030273437555 +1.26125 0.267852783203125 0.16925048828125 -0.3681030273437555 +1.2613750000000002 0.27227783203125 0.172027587890625 -0.3681030273437555 +1.2615 0.27227783203125 0.172027587890625 -0.3681030273437555 +1.261625 0.276641845703125 0.174774169921875 -0.3681030273437555 +1.26175 0.280975341796875 0.177520751953125 -0.3681030273437555 +1.261875 0.280975341796875 0.177520751953125 -0.3681030273437555 +1.262 0.285308837890625 0.180267333984375 -0.3681030273437555 +1.262125 0.285308837890625 0.180267333984375 -0.3681030273437555 +1.26225 0.289581298828125 0.1829833984375 -0.3681030273437555 +1.262375 0.2938232421875 0.185638427734375 -0.3681030273437555 +1.2625 0.2938232421875 0.185638427734375 -0.3681030273437555 +1.262625 0.298065185546875 0.188323974609375 -0.3681030273437555 +1.26275 0.298065185546875 0.188323974609375 -0.3681030273437555 +1.262875 0.30224609375 0.19097900390625 -0.3681030273437555 +1.2630000000000002 0.306396484375 0.193603515625 -0.3681030273437555 +1.263125 0.306396484375 0.193603515625 -0.3681030273437555 +1.26325 0.310516357421875 0.196197509765625 -0.3681030273437555 +1.2633750000000002 0.310516357421875 0.196197509765625 -0.3681030273437555 +1.2635 0.314605712890625 0.198760986328125 -0.3681030273437555 +1.263625 0.31866455078125 0.201324462890625 -0.3681030273437555 +1.26375 0.31866455078125 0.201324462890625 -0.3681030273437555 +1.263875 0.322662353515625 0.203887939453125 -0.3681030273437555 +1.264 0.322662353515625 0.203887939453125 -0.3681030273437555 +1.264125 0.32666015625 0.206390380859375 -0.3681030273437555 +1.26425 0.330596923828125 0.208892822265625 -0.3681030273437555 +1.264375 0.330596923828125 0.208892822265625 -0.3681030273437555 +1.2645 0.334503173828125 0.21136474609375 -0.3681030273437555 +1.264625 0.334503173828125 0.21136474609375 -0.3681030273437555 +1.26475 0.33837890625 0.21380615234375 -0.3681030273437555 +1.264875 0.34222412109375 0.216217041015625 -0.3681030273437555 +1.2650000000000002 0.34222412109375 0.216217041015625 -0.3681030273437555 +1.265125 0.34600830078125 0.2186279296875 -0.3681030273437555 +1.26525 0.34600830078125 0.2186279296875 -0.3681030273437555 +1.265375 0.349761962890625 0.22100830078125 -0.3681030273437555 +1.2655 0.353485107421875 0.223358154296875 -0.3681030273437555 +1.265625 0.353485107421875 0.223358154296875 -0.3681030273437555 +1.26575 0.357177734375 0.225677490234375 -0.3681030273437555 +1.265875 0.357177734375 0.225677490234375 -0.3681030273437555 +1.266 0.360809326171875 0.22796630859375 -0.3681030273437555 +1.266125 0.36444091796875 0.230255126953125 -0.3681030273437555 +1.26625 0.36444091796875 0.230255126953125 -0.3681030273437555 +1.266375 0.36798095703125 0.232513427734375 -0.3681030273437555 +1.2665 0.36798095703125 0.232513427734375 -0.3681030273437555 +1.2666250000000002 0.37152099609375 0.2347412109375 -0.3681030273437555 +1.26675 0.375 0.2369384765625 -0.3681030273437555 +1.266875 0.375 0.2369384765625 -0.3681030273437555 +1.2670000000000002 0.378448486328125 0.239105224609375 -0.3681030273437555 +1.267125 0.378448486328125 0.239105224609375 -0.3681030273437555 +1.26725 0.3818359375 0.24127197265625 -0.3681030273437555 +1.267375 0.38519287109375 0.243377685546875 -0.3681030273437555 +1.2675 0.38519287109375 0.243377685546875 -0.3681030273437555 +1.267625 0.388519287109375 0.2454833984375 -0.3681030273437555 +1.26775 0.388519287109375 0.2454833984375 -0.3681030273437555 +1.267875 0.39178466796875 0.24755859375 -0.3681030273437555 +1.268 0.39501953125 0.249603271484375 -0.3681030273437555 +1.268125 0.39501953125 0.249603271484375 -0.3681030273437555 +1.26825 0.398193359375 0.2515869140625 -0.3681030273437555 +1.268375 0.398193359375 0.2515869140625 -0.3681030273437555 +1.2685 0.401336669921875 0.253570556640625 -0.3681030273437555 +1.2686250000000002 0.404449462890625 0.25555419921875 -0.3681030273437555 +1.26875 0.404449462890625 0.25555419921875 -0.3681030273437555 +1.268875 0.407501220703125 0.257476806640625 -0.3681030273437555 +1.2690000000000002 0.407501220703125 0.257476806640625 -0.3681030273437555 +1.269125 0.4105224609375 0.259368896484375 -0.3681030273437555 +1.26925 0.413482666015625 0.26123046875 -0.3681030273437555 +1.269375 0.413482666015625 0.26123046875 -0.3681030273437555 +1.2695 0.416412353515625 0.263092041015625 -0.3681030273437555 +1.269625 0.416412353515625 0.263092041015625 -0.3681030273437555 +1.26975 0.419281005859375 0.264923095703125 -0.3681030273437555 +1.269875 0.422119140625 0.266693115234375 -0.3681030273437555 +1.27 0.422119140625 0.266693115234375 -0.3681030273437555 +1.270125 0.424896240234375 0.268463134765625 -0.3681030273437555 +1.27025 0.424896240234375 0.268463134765625 -0.3681030273437555 +1.270375 0.4276123046875 0.270172119140625 -0.3681030273437555 +1.2705 0.4302978515625 0.271881103515625 -0.3681030273437555 +1.2706250000000002 0.4302978515625 0.271881103515625 -0.3681030273437555 +1.27075 0.432952880859375 0.2735595703125 -0.3681030273437555 +1.270875 0.432952880859375 0.2735595703125 -0.3681030273437555 +1.2710000000000002 0.435546875 0.27520751953125 -0.3681030273437555 +1.271125 0.438079833984375 0.27679443359375 -0.3681030273437555 +1.27125 0.438079833984375 0.27679443359375 -0.3681030273437555 +1.271375 0.440582275390625 0.27838134765625 -0.3681030273437555 +1.2715 0.440582275390625 0.27838134765625 -0.3681030273437555 +1.271625 0.44305419921875 0.279937744140625 -0.3681030273437555 +1.27175 0.4454345703125 0.28143310546875 -0.3681030273437555 +1.271875 0.4454345703125 0.28143310546875 -0.3681030273437555 +1.272 0.447784423828125 0.282928466796875 -0.3681030273437555 +1.272125 0.447784423828125 0.282928466796875 -0.3681030273437555 +1.27225 0.450103759765625 0.284393310546875 -0.3681030273437555 +1.272375 0.452362060546875 0.285797119140625 -0.3681030273437555 +1.2725 0.452362060546875 0.285797119140625 -0.3681030273437555 +1.2726250000000002 0.454559326171875 0.287200927734375 -0.3681030273437555 +1.27275 0.454559326171875 0.287200927734375 -0.3681030273437555 +1.272875 0.45672607421875 0.28857421875 -0.3681030273437555 +1.2730000000000002 0.458831787109375 0.289886474609375 -0.3681030273437555 +1.273125 0.458831787109375 0.289886474609375 -0.3681030273437555 +1.27325 0.46087646484375 0.29119873046875 -0.3681030273437555 +1.273375 0.46087646484375 0.29119873046875 -0.3681030273437555 +1.2735 0.462890625 0.292449951171875 -0.3681030273437555 +1.273625 0.464813232421875 0.293670654296875 -0.3681030273437555 +1.27375 0.464813232421875 0.293670654296875 -0.3681030273437555 +1.273875 0.46673583984375 0.294891357421875 -0.3681030273437555 +1.274 0.46673583984375 0.294891357421875 -0.3681030273437555 +1.274125 0.46856689453125 0.296051025390625 -0.3681030273437555 +1.27425 0.470367431640625 0.29718017578125 -0.3681030273437555 +1.274375 0.470367431640625 0.29718017578125 -0.3681030273437555 +1.2745 0.472137451171875 0.298309326171875 -0.3681030273437555 +1.2746250000000002 0.472137451171875 0.298309326171875 -0.3681030273437555 +1.27475 0.47381591796875 0.29937744140625 -0.3681030273437555 +1.274875 0.4754638671875 0.3004150390625 -0.3681030273437555 +1.2750000000000002 0.4754638671875 0.3004150390625 -0.3681030273437555 +1.275125 0.47705078125 0.301422119140625 -0.3681030273437555 +1.27525 0.47705078125 0.301422119140625 -0.3681030273437555 +1.275375 0.478607177734375 0.302398681640625 -0.3681030273437555 +1.2755 0.480072021484375 0.303314208984375 -0.3681030273437555 +1.275625 0.480072021484375 0.303314208984375 -0.3681030273437555 +1.27575 0.48150634765625 0.304229736328125 -0.3681030273437555 +1.275875 0.48150634765625 0.304229736328125 -0.3681030273437555 +1.276 0.48291015625 0.30511474609375 -0.3681030273437555 +1.276125 0.484222412109375 0.305938720703125 -0.3681030273437555 +1.27625 0.484222412109375 0.305938720703125 -0.3681030273437555 +1.276375 0.485504150390625 0.3067626953125 -0.3681030273437555 +1.2765 0.485504150390625 0.3067626953125 -0.3681030273437555 +1.2766250000000002 0.486724853515625 0.307525634765625 -0.3681030273437555 +1.27675 0.487884521484375 0.308258056640625 -0.3681030273437555 +1.276875 0.487884521484375 0.308258056640625 -0.3681030273437555 +1.2770000000000002 0.489013671875 0.3089599609375 -0.3681030273437555 +1.277125 0.489013671875 0.3089599609375 -0.3681030273437555 +1.27725 0.490081787109375 0.30963134765625 -0.3681030273437555 +1.277375 0.4910888671875 0.310272216796875 -0.3681030273437555 +1.2775 0.4910888671875 0.310272216796875 -0.3681030273437555 +1.277625 0.492034912109375 0.310882568359375 -0.3681030273437555 +1.27775 0.492034912109375 0.310882568359375 -0.3681030273437555 +1.277875 0.492950439453125 0.31146240234375 -0.3681030273437555 +1.278 0.4937744140625 0.311981201171875 -0.3681030273437555 +1.278125 0.4937744140625 0.311981201171875 -0.3681030273437555 +1.27825 0.49456787109375 0.312469482421875 -0.3681030273437555 +1.278375 0.49456787109375 0.312469482421875 -0.3681030273437555 +1.2785 0.49530029296875 0.312957763671875 -0.3681030273437555 +1.2786250000000002 0.496002197265625 0.313385009765625 -0.3681030273437555 +1.27875 0.496002197265625 0.313385009765625 -0.3681030273437555 +1.278875 0.496612548828125 0.31378173828125 -0.3681030273437555 +1.2790000000000002 0.496612548828125 0.31378173828125 -0.3681030273437555 +1.279125 0.4971923828125 0.31414794921875 -0.3681030273437555 +1.27925 0.497711181640625 0.314483642578125 -0.3681030273437555 +1.279375 0.497711181640625 0.314483642578125 -0.3681030273437555 +1.2795 0.498199462890625 0.31475830078125 -0.3681030273437555 +1.279625 0.498199462890625 0.31475830078125 -0.3681030273437555 +1.27975 0.49859619140625 0.315032958984375 -0.3681030273437555 +1.279875 0.49896240234375 0.31524658203125 -0.3681030273437555 +1.28 0.49896240234375 0.148345947265625 -0.7025939941406295 +1.280125 0.499267578125 0.1484375 -0.7025939941406295 +1.28025 0.499267578125 0.1484375 -0.7025939941406295 +1.280375 0.49951171875 0.14849853515625 -0.7025939941406295 +1.2805 0.49969482421875 0.1485595703125 -0.7025939941406295 +1.2806250000000002 0.49969482421875 0.1485595703125 -0.7025939941406295 +1.28075 0.49981689453125 0.148590087890625 -0.7025939941406295 +1.280875 0.49981689453125 0.148590087890625 -0.7025939941406295 +1.281 0.499908447265625 0.14862060546875 -0.7025939941406295 +1.281125 0.49993896484375 0.14862060546875 -0.7025939941406295 +1.28125 0.49993896484375 0.14862060546875 -0.7025939941406295 +1.281375 0.499908447265625 0.14862060546875 -0.7025939941406295 +1.2815 0.499908447265625 0.14862060546875 -0.7025939941406295 +1.281625 0.49981689453125 0.148590087890625 -0.7025939941406295 +1.28175 0.49969482421875 0.1485595703125 -0.7025939941406295 +1.281875 0.49969482421875 0.1485595703125 -0.7025939941406295 +1.282 0.49951171875 0.14849853515625 -0.7025939941406295 +1.282125 0.49951171875 0.14849853515625 -0.7025939941406295 +1.2822500000000002 0.499267578125 0.1484375 -0.7025939941406295 +1.282375 0.49896240234375 0.148345947265625 -0.7025939941406295 +1.2825 0.49896240234375 0.148345947265625 -0.7025939941406295 +1.2826250000000002 0.49859619140625 0.148223876953125 -0.7025939941406295 +1.28275 0.49859619140625 0.148223876953125 -0.7025939941406295 +1.282875 0.498199462890625 0.148101806640625 -0.7025939941406295 +1.283 0.497711181640625 0.147979736328125 -0.7025939941406295 +1.283125 0.497711181640625 0.147979736328125 -0.7025939941406295 +1.28325 0.4971923828125 0.1478271484375 -0.7025939941406295 +1.283375 0.4971923828125 0.1478271484375 -0.7025939941406295 +1.2835 0.496612548828125 0.14764404296875 -0.7025939941406295 +1.283625 0.496002197265625 0.1474609375 -0.7025939941406295 +1.28375 0.496002197265625 0.1474609375 -0.7025939941406295 +1.283875 0.49530029296875 0.147247314453125 -0.7025939941406295 +1.284 0.49530029296875 0.147247314453125 -0.7025939941406295 +1.284125 0.49456787109375 0.14703369140625 -0.7025939941406295 +1.2842500000000002 0.4937744140625 0.14678955078125 -0.7025939941406295 +1.284375 0.4937744140625 0.14678955078125 -0.7025939941406295 +1.2845 0.492950439453125 0.14654541015625 -0.7025939941406295 +1.2846250000000002 0.492950439453125 0.14654541015625 -0.7025939941406295 +1.28475 0.492034912109375 0.146270751953125 -0.7025939941406295 +1.284875 0.4910888671875 0.14599609375 -0.7025939941406295 +1.285 0.4910888671875 0.14599609375 -0.7025939941406295 +1.285125 0.490081787109375 0.14569091796875 -0.7025939941406295 +1.28525 0.490081787109375 0.14569091796875 -0.7025939941406295 +1.285375 0.489013671875 0.1453857421875 -0.7025939941406295 +1.2855 0.487884521484375 0.145050048828125 -0.7025939941406295 +1.285625 0.487884521484375 0.145050048828125 -0.7025939941406295 +1.28575 0.486724853515625 0.14471435546875 -0.7025939941406295 +1.285875 0.486724853515625 0.14471435546875 -0.7025939941406295 +1.286 0.485504150390625 0.14434814453125 -0.7025939941406295 +1.286125 0.484222412109375 0.143951416015625 -0.7025939941406295 +1.2862500000000002 0.484222412109375 0.143951416015625 -0.7025939941406295 +1.286375 0.48291015625 0.1435546875 -0.7025939941406295 +1.2865 0.48291015625 0.1435546875 -0.7025939941406295 +1.2866250000000002 0.48150634765625 0.143157958984375 -0.7025939941406295 +1.28675 0.480072021484375 0.142730712890625 -0.7025939941406295 +1.286875 0.480072021484375 0.142730712890625 -0.7025939941406295 +1.287 0.478607177734375 0.14227294921875 -0.7025939941406295 +1.287125 0.478607177734375 0.14227294921875 -0.7025939941406295 +1.28725 0.47705078125 0.141815185546875 -0.7025939941406295 +1.287375 0.4754638671875 0.141357421875 -0.7025939941406295 +1.2875 0.4754638671875 0.141357421875 -0.7025939941406295 +1.287625 0.47381591796875 0.140869140625 -0.7025939941406295 +1.28775 0.47381591796875 0.140869140625 -0.7025939941406295 +1.287875 0.472137451171875 0.140350341796875 -0.7025939941406295 +1.288 0.470367431640625 0.13983154296875 -0.7025939941406295 +1.288125 0.470367431640625 0.13983154296875 -0.7025939941406295 +1.2882500000000002 0.46856689453125 0.139312744140625 -0.7025939941406295 +1.288375 0.46856689453125 0.139312744140625 -0.7025939941406295 +1.2885 0.46673583984375 0.138763427734375 -0.7025939941406295 +1.2886250000000002 0.464813232421875 0.13818359375 -0.7025939941406295 +1.28875 0.464813232421875 0.13818359375 -0.7025939941406295 +1.288875 0.462890625 0.137603759765625 -0.7025939941406295 +1.289 0.462890625 0.137603759765625 -0.7025939941406295 +1.289125 0.46087646484375 0.13702392578125 -0.7025939941406295 +1.28925 0.458831787109375 0.13641357421875 -0.7025939941406295 +1.289375 0.458831787109375 0.13641357421875 -0.7025939941406295 +1.2895 0.45672607421875 0.135772705078125 -0.7025939941406295 +1.289625 0.45672607421875 0.135772705078125 -0.7025939941406295 +1.28975 0.454559326171875 0.1351318359375 -0.7025939941406295 +1.289875 0.452362060546875 0.134490966796875 -0.7025939941406295 +1.29 0.452362060546875 0.134490966796875 -0.7025939941406295 +1.290125 0.450103759765625 0.133819580078125 -0.7025939941406295 +1.2902500000000002 0.450103759765625 0.133819580078125 -0.7025939941406295 +1.290375 0.447784423828125 0.13311767578125 -0.7025939941406295 +1.2905 0.4454345703125 0.132415771484375 -0.7025939941406295 +1.2906250000000002 0.4454345703125 0.132415771484375 -0.7025939941406295 +1.29075 0.44305419921875 0.1317138671875 -0.7025939941406295 +1.290875 0.44305419921875 0.1317138671875 -0.7025939941406295 +1.291 0.440582275390625 0.1309814453125 -0.7025939941406295 +1.291125 0.438079833984375 0.1302490234375 -0.7025939941406295 +1.29125 0.438079833984375 0.1302490234375 -0.7025939941406295 +1.291375 0.435546875 0.129486083984375 -0.7025939941406295 +1.2915 0.435546875 0.129486083984375 -0.7025939941406295 +1.291625 0.432952880859375 0.12872314453125 -0.7025939941406295 +1.29175 0.4302978515625 0.1279296875 -0.7025939941406295 +1.291875 0.4302978515625 0.1279296875 -0.7025939941406295 +1.292 0.4276123046875 0.12713623046875 -0.7025939941406295 +1.292125 0.4276123046875 0.12713623046875 -0.7025939941406295 +1.2922500000000002 0.424896240234375 0.126312255859375 -0.7025939941406295 +1.292375 0.422119140625 0.12548828125 -0.7025939941406295 +1.2925 0.422119140625 0.12548828125 -0.7025939941406295 +1.2926250000000002 0.419281005859375 0.1246337890625 -0.7025939941406295 +1.29275 0.419281005859375 0.1246337890625 -0.7025939941406295 +1.292875 0.416412353515625 0.123779296875 -0.7025939941406295 +1.293 0.413482666015625 0.1229248046875 -0.7025939941406295 +1.293125 0.413482666015625 0.1229248046875 -0.7025939941406295 +1.29325 0.4105224609375 0.122039794921875 -0.7025939941406295 +1.293375 0.4105224609375 0.122039794921875 -0.7025939941406295 +1.2935 0.407501220703125 0.12115478515625 -0.7025939941406295 +1.293625 0.404449462890625 0.1202392578125 -0.7025939941406295 +1.29375 0.404449462890625 0.1202392578125 -0.7025939941406295 +1.293875 0.401336669921875 0.11932373046875 -0.7025939941406295 +1.294 0.401336669921875 0.11932373046875 -0.7025939941406295 +1.294125 0.398193359375 0.118377685546875 -0.7025939941406295 +1.2942500000000002 0.39501953125 0.117431640625 -0.7025939941406295 +1.294375 0.39501953125 0.117431640625 -0.7025939941406295 +1.2945 0.39178466796875 0.116485595703125 -0.7025939941406295 +1.2946250000000002 0.39178466796875 0.116485595703125 -0.7025939941406295 +1.29475 0.388519287109375 0.115509033203125 -0.7025939941406295 +1.294875 0.38519287109375 0.114501953125 -0.7025939941406295 +1.295 0.38519287109375 0.114501953125 -0.7025939941406295 +1.295125 0.3818359375 0.113525390625 -0.7025939941406295 +1.29525 0.3818359375 0.113525390625 -0.7025939941406295 +1.295375 0.378448486328125 0.112518310546875 -0.7025939941406295 +1.2955 0.375 0.111480712890625 -0.7025939941406295 +1.295625 0.375 0.111480712890625 -0.7025939941406295 +1.29575 0.37152099609375 0.110443115234375 -0.7025939941406295 +1.295875 0.37152099609375 0.110443115234375 -0.7025939941406295 +1.296 0.36798095703125 0.109405517578125 -0.7025939941406295 +1.296125 0.36444091796875 0.10833740234375 -0.7025939941406295 +1.2962500000000002 0.36444091796875 0.10833740234375 -0.7025939941406295 +1.296375 0.360809326171875 0.107269287109375 -0.7025939941406295 +1.2965 0.360809326171875 0.107269287109375 -0.7025939941406295 +1.296625 0.357177734375 0.106201171875 -0.7025939941406295 +1.29675 0.353485107421875 0.1051025390625 -0.7025939941406295 +1.296875 0.353485107421875 0.1051025390625 -0.7025939941406295 +1.297 0.349761962890625 0.103973388671875 -0.7025939941406295 +1.297125 0.349761962890625 0.103973388671875 -0.7025939941406295 +1.29725 0.34600830078125 0.102874755859375 -0.7025939941406295 +1.297375 0.34222412109375 0.10174560546875 -0.7025939941406295 +1.2975 0.34222412109375 0.10174560546875 -0.7025939941406295 +1.297625 0.33837890625 0.1005859375 -0.7025939941406295 +1.29775 0.33837890625 0.1005859375 -0.7025939941406295 +1.2978750000000002 0.334503173828125 0.099456787109375 -0.7025939941406295 +1.298 0.330596923828125 0.098297119140625 -0.7025939941406295 +1.298125 0.330596923828125 0.098297119140625 -0.7025939941406295 +1.2982500000000002 0.32666015625 0.09710693359375 -0.7025939941406295 +1.298375 0.32666015625 0.09710693359375 -0.7025939941406295 +1.2985 0.322662353515625 0.095916748046875 -0.7025939941406295 +1.298625 0.31866455078125 0.0947265625 -0.7025939941406295 +1.29875 0.31866455078125 0.0947265625 -0.7025939941406295 +1.298875 0.314605712890625 0.093536376953125 -0.7025939941406295 +1.299 0.314605712890625 0.093536376953125 -0.7025939941406295 +1.299125 0.310516357421875 0.092315673828125 -0.7025939941406295 +1.29925 0.306396484375 0.091094970703125 -0.7025939941406295 +1.299375 0.306396484375 0.091094970703125 -0.7025939941406295 +1.2995 0.30224609375 0.08984375 -0.7025939941406295 +1.299625 0.30224609375 0.08984375 -0.7025939941406295 +1.29975 0.298065185546875 0.088623046875 -0.7025939941406295 +1.2998750000000002 0.2938232421875 0.08734130859375 -0.7025939941406295 +1.3 0.2938232421875 0.08734130859375 -0.7025939941406295 +1.300125 0.289581298828125 0.086090087890625 -0.7025939941406295 +1.3002500000000002 0.289581298828125 0.086090087890625 -0.7025939941406295 +1.300375 0.285308837890625 0.084808349609375 -0.7025939941406295 +1.3005 0.280975341796875 0.083526611328125 -0.7025939941406295 +1.300625 0.280975341796875 0.083526611328125 -0.7025939941406295 +1.30075 0.276641845703125 0.082244873046875 -0.7025939941406295 +1.300875 0.276641845703125 0.082244873046875 -0.7025939941406295 +1.301 0.27227783203125 0.0809326171875 -0.7025939941406295 +1.301125 0.267852783203125 0.079620361328125 -0.7025939941406295 +1.30125 0.267852783203125 0.079620361328125 -0.7025939941406295 +1.301375 0.263427734375 0.07830810546875 -0.7025939941406295 +1.3015 0.263427734375 0.07830810546875 -0.7025939941406295 +1.301625 0.25897216796875 0.076995849609375 -0.7025939941406295 +1.30175 0.25445556640625 0.075653076171875 -0.7025939941406295 +1.3018750000000002 0.25445556640625 0.075653076171875 -0.7025939941406295 +1.302 0.249969482421875 0.074310302734375 -0.7025939941406295 +1.302125 0.249969482421875 0.074310302734375 -0.7025939941406295 +1.3022500000000002 0.24542236328125 0.072967529296875 -0.7025939941406295 +1.302375 0.2408447265625 0.07159423828125 -0.7025939941406295 +1.3025 0.2408447265625 0.07159423828125 -0.7025939941406295 +1.302625 0.236236572265625 0.070220947265625 -0.7025939941406295 +1.30275 0.236236572265625 0.070220947265625 -0.7025939941406295 +1.302875 0.231597900390625 0.06884765625 -0.7025939941406295 +1.303 0.226959228515625 0.067474365234375 -0.7025939941406295 +1.303125 0.226959228515625 0.067474365234375 -0.7025939941406295 +1.30325 0.2222900390625 0.066070556640625 -0.7025939941406295 +1.303375 0.2222900390625 0.066070556640625 -0.7025939941406295 +1.3035 0.21759033203125 0.064666748046875 -0.7025939941406295 +1.303625 0.212860107421875 0.063262939453125 -0.7025939941406295 +1.30375 0.212860107421875 0.063262939453125 -0.7025939941406295 +1.3038750000000002 0.208099365234375 0.061859130859375 -0.7025939941406295 +1.304 0.208099365234375 0.061859130859375 -0.7025939941406295 +1.304125 0.203338623046875 0.060455322265625 -0.7025939941406295 +1.3042500000000002 0.19854736328125 0.05902099609375 -0.7025939941406295 +1.304375 0.19854736328125 0.05902099609375 -0.7025939941406295 +1.3045 0.1937255859375 0.057586669921875 -0.7025939941406295 +1.304625 0.1937255859375 0.057586669921875 -0.7025939941406295 +1.30475 0.188873291015625 0.05615234375 -0.7025939941406295 +1.304875 0.18402099609375 0.054718017578125 -0.7025939941406295 +1.305 0.18402099609375 0.054718017578125 -0.7025939941406295 +1.305125 0.17913818359375 0.053253173828125 -0.7025939941406295 +1.30525 0.17913818359375 0.053253173828125 -0.7025939941406295 +1.305375 0.17425537109375 0.051788330078125 -0.7025939941406295 +1.3055 0.169342041015625 0.050323486328125 -0.7025939941406295 +1.305625 0.169342041015625 0.050323486328125 -0.7025939941406295 +1.30575 0.164398193359375 0.048858642578125 -0.7025939941406295 +1.3058750000000002 0.164398193359375 0.048858642578125 -0.7025939941406295 +1.306 0.159454345703125 0.047393798828125 -0.7025939941406295 +1.306125 0.15447998046875 0.045928955078125 -0.7025939941406295 +1.3062500000000002 0.15447998046875 0.045928955078125 -0.7025939941406295 +1.306375 0.14947509765625 0.04443359375 -0.7025939941406295 +1.3065 0.14947509765625 0.04443359375 -0.7025939941406295 +1.306625 0.14447021484375 0.042938232421875 -0.7025939941406295 +1.30675 0.13946533203125 0.04144287109375 -0.7025939941406295 +1.306875 0.13946533203125 0.04144287109375 -0.7025939941406295 +1.307 0.134429931640625 0.039947509765625 -0.7025939941406295 +1.307125 0.134429931640625 0.039947509765625 -0.7025939941406295 +1.30725 0.129364013671875 0.0384521484375 -0.7025939941406295 +1.307375 0.124298095703125 0.036956787109375 -0.7025939941406295 +1.3075 0.124298095703125 0.036956787109375 -0.7025939941406295 +1.307625 0.119232177734375 0.035430908203125 -0.7025939941406295 +1.30775 0.119232177734375 0.035430908203125 -0.7025939941406295 +1.3078750000000002 0.1141357421875 0.033935546875 -0.7025939941406295 +1.308 0.109039306640625 0.03240966796875 -0.7025939941406295 +1.308125 0.109039306640625 0.03240966796875 -0.7025939941406295 +1.3082500000000002 0.103912353515625 0.0308837890625 -0.7025939941406295 +1.308375 0.103912353515625 0.0308837890625 -0.7025939941406295 +1.3085 0.098785400390625 0.02935791015625 -0.7025939941406295 +1.308625 0.093658447265625 0.02783203125 -0.7025939941406295 +1.30875 0.093658447265625 0.02783203125 -0.7025939941406295 +1.308875 0.0885009765625 0.02630615234375 -0.7025939941406295 +1.309 0.0885009765625 0.02630615234375 -0.7025939941406295 +1.309125 0.083343505859375 0.0247802734375 -0.7025939941406295 +1.30925 0.07818603515625 0.023223876953125 -0.7025939941406295 +1.309375 0.07818603515625 0.023223876953125 -0.7025939941406295 +1.3095 0.072998046875 0.021697998046875 -0.7025939941406295 +1.309625 0.072998046875 0.021697998046875 -0.7025939941406295 +1.30975 0.06781005859375 0.0201416015625 -0.7025939941406295 +1.3098750000000002 0.0626220703125 0.01861572265625 -0.7025939941406295 +1.31 0.0626220703125 0.01861572265625 -0.7025939941406295 +1.310125 0.05743408203125 0.017059326171875 -0.7025939941406295 +1.3102500000000002 0.05743408203125 0.017059326171875 -0.7025939941406295 +1.310375 0.05224609375 0.015533447265625 -0.7025939941406295 +1.3105 0.047027587890625 0.01397705078125 -0.7025939941406295 +1.310625 0.047027587890625 0.01397705078125 -0.7025939941406295 +1.31075 0.04180908203125 0.012420654296875 -0.7025939941406295 +1.310875 0.04180908203125 0.012420654296875 -0.7025939941406295 +1.311 0.036590576171875 0.0108642578125 -0.7025939941406295 +1.311125 0.0313720703125 0.009307861328125 -0.7025939941406295 +1.31125 0.0313720703125 0.009307861328125 -0.7025939941406295 +1.311375 0.026123046875 0.00775146484375 -0.7025939941406295 +1.3115 0.026123046875 0.00775146484375 -0.7025939941406295 +1.311625 0.020904541015625 0.006195068359375 -0.7025939941406295 +1.31175 0.01568603515625 0.004638671875 -0.7025939941406295 +1.3118750000000002 0.01568603515625 0.004638671875 -0.7025939941406295 +1.312 0.01043701171875 0.000762939453125 -0.9250366210937526 +1.312125 0.01043701171875 0.000762939453125 -0.9250366210937526 +1.31225 0.005218505859375 0.0003662109375 -0.9250366210937526 +1.312375 0.0 0.0 -0.9250366210937526 +1.3125 0.0 0.0 -0.9250366210937526 +1.312625 -0.005218505859375 -0.0003662109375 -0.9250366210937526 +1.31275 -0.005218505859375 -0.0003662109375 -0.9250366210937526 +1.312875 -0.01043701171875 -0.000762939453125 -0.9250366210937526 +1.313 -0.01568603515625 -0.00115966796875 -0.9250366210937526 +1.313125 -0.01568603515625 -0.00115966796875 -0.9250366210937526 +1.31325 -0.020904541015625 -0.001556396484375 -0.9250366210937526 +1.313375 -0.020904541015625 -0.001556396484375 -0.9250366210937526 +1.3135000000000002 -0.026123046875 -0.001953125 -0.9250366210937526 +1.313625 -0.0313720703125 -0.0023193359375 -0.9250366210937526 +1.31375 -0.0313720703125 -0.0023193359375 -0.9250366210937526 +1.3138750000000002 -0.036590576171875 -0.002716064453125 -0.9250366210937526 +1.314 -0.036590576171875 -0.002716064453125 -0.9250366210937526 +1.314125 -0.04180908203125 -0.00311279296875 -0.9250366210937526 +1.31425 -0.047027587890625 -0.003509521484375 -0.9250366210937526 +1.314375 -0.047027587890625 -0.003509521484375 -0.9250366210937526 +1.3145 -0.05224609375 -0.00390625 -0.9250366210937526 +1.314625 -0.05224609375 -0.00390625 -0.9250366210937526 +1.31475 -0.05743408203125 -0.0042724609375 -0.9250366210937526 +1.314875 -0.0626220703125 -0.004669189453125 -0.9250366210937526 +1.315 -0.0626220703125 -0.004669189453125 -0.9250366210937526 +1.315125 -0.06781005859375 -0.00506591796875 -0.9250366210937526 +1.31525 -0.06781005859375 -0.00506591796875 -0.9250366210937526 +1.315375 -0.072998046875 -0.005462646484375 -0.9250366210937526 +1.3155000000000002 -0.07818603515625 -0.005828857421875 -0.9250366210937526 +1.315625 -0.07818603515625 -0.005828857421875 -0.9250366210937526 +1.31575 -0.083343505859375 -0.0062255859375 -0.9250366210937526 +1.3158750000000002 -0.083343505859375 -0.0062255859375 -0.9250366210937526 +1.316 -0.0885009765625 -0.006622314453125 -0.9250366210937526 +1.316125 -0.093658447265625 -0.006988525390625 -0.9250366210937526 +1.31625 -0.093658447265625 -0.006988525390625 -0.9250366210937526 +1.316375 -0.098785400390625 -0.00738525390625 -0.9250366210937526 +1.3165 -0.098785400390625 -0.00738525390625 -0.9250366210937526 +1.316625 -0.103912353515625 -0.00775146484375 -0.9250366210937526 +1.31675 -0.109039306640625 -0.008148193359375 -0.9250366210937526 +1.316875 -0.109039306640625 -0.008148193359375 -0.9250366210937526 +1.317 -0.1141357421875 -0.008544921875 -0.9250366210937526 +1.317125 -0.1141357421875 -0.008544921875 -0.9250366210937526 +1.31725 -0.119232177734375 -0.0089111328125 -0.9250366210937526 +1.317375 -0.124298095703125 -0.00927734375 -0.9250366210937526 +1.3175000000000002 -0.124298095703125 -0.00927734375 -0.9250366210937526 +1.317625 -0.129364013671875 -0.009674072265625 -0.9250366210937526 +1.31775 -0.129364013671875 -0.009674072265625 -0.9250366210937526 +1.3178750000000002 -0.134429931640625 -0.010040283203125 -0.9250366210937526 +1.318 -0.13946533203125 -0.01043701171875 -0.9250366210937526 +1.318125 -0.13946533203125 -0.01043701171875 -0.9250366210937526 +1.31825 -0.14447021484375 -0.01080322265625 -0.9250366210937526 +1.318375 -0.14447021484375 -0.01080322265625 -0.9250366210937526 +1.3185 -0.14947509765625 -0.01116943359375 -0.9250366210937526 +1.318625 -0.15447998046875 -0.01153564453125 -0.9250366210937526 +1.31875 -0.15447998046875 -0.01153564453125 -0.9250366210937526 +1.318875 -0.159454345703125 -0.011932373046875 -0.9250366210937526 +1.319 -0.159454345703125 -0.011932373046875 -0.9250366210937526 +1.319125 -0.164398193359375 -0.012298583984375 -0.9250366210937526 +1.31925 -0.169342041015625 -0.012664794921875 -0.9250366210937526 +1.319375 -0.169342041015625 -0.012664794921875 -0.9250366210937526 +1.3195000000000002 -0.17425537109375 -0.013031005859375 -0.9250366210937526 +1.319625 -0.17425537109375 -0.013031005859375 -0.9250366210937526 +1.31975 -0.17913818359375 -0.013397216796875 -0.9250366210937526 +1.3198750000000002 -0.18402099609375 -0.013763427734375 -0.9250366210937526 +1.32 -0.18402099609375 -0.013763427734375 -0.9250366210937526 +1.320125 -0.188873291015625 -0.014129638671875 -0.9250366210937526 +1.32025 -0.188873291015625 -0.014129638671875 -0.9250366210937526 +1.320375 -0.1937255859375 -0.014495849609375 -0.9250366210937526 +1.3205 -0.19854736328125 -0.014862060546875 -0.9250366210937526 +1.320625 -0.19854736328125 -0.014862060546875 -0.9250366210937526 +1.32075 -0.203338623046875 -0.01519775390625 -0.9250366210937526 +1.320875 -0.203338623046875 -0.01519775390625 -0.9250366210937526 +1.321 -0.208099365234375 -0.01556396484375 -0.9250366210937526 +1.321125 -0.212860107421875 -0.01593017578125 -0.9250366210937526 +1.32125 -0.212860107421875 -0.01593017578125 -0.9250366210937526 +1.321375 -0.21759033203125 -0.016265869140625 -0.9250366210937526 +1.3215000000000002 -0.21759033203125 -0.016265869140625 -0.9250366210937526 +1.321625 -0.2222900390625 -0.016632080078125 -0.9250366210937526 +1.32175 -0.226959228515625 -0.0169677734375 -0.9250366210937526 +1.3218750000000002 -0.226959228515625 -0.0169677734375 -0.9250366210937526 +1.322 -0.231597900390625 -0.017333984375 -0.9250366210937526 +1.322125 -0.231597900390625 -0.017333984375 -0.9250366210937526 +1.32225 -0.236236572265625 -0.017669677734375 -0.9250366210937526 +1.322375 -0.2408447265625 -0.01800537109375 -0.9250366210937526 +1.3225 -0.2408447265625 -0.01800537109375 -0.9250366210937526 +1.322625 -0.24542236328125 -0.01837158203125 -0.9250366210937526 +1.32275 -0.24542236328125 -0.01837158203125 -0.9250366210937526 +1.322875 -0.249969482421875 -0.018707275390625 -0.9250366210937526 +1.323 -0.25445556640625 -0.01904296875 -0.9250366210937526 +1.323125 -0.25445556640625 -0.01904296875 -0.9250366210937526 +1.32325 -0.25897216796875 -0.019378662109375 -0.9250366210937526 +1.323375 -0.25897216796875 -0.019378662109375 -0.9250366210937526 +1.3235000000000002 -0.263427734375 -0.01971435546875 -0.9250366210937526 +1.323625 -0.267852783203125 -0.020050048828125 -0.9250366210937526 +1.32375 -0.267852783203125 -0.020050048828125 -0.9250366210937526 +1.3238750000000002 -0.27227783203125 -0.020355224609375 -0.9250366210937526 +1.324 -0.27227783203125 -0.020355224609375 -0.9250366210937526 +1.324125 -0.276641845703125 -0.02069091796875 -0.9250366210937526 +1.32425 -0.280975341796875 -0.021026611328125 -0.9250366210937526 +1.324375 -0.280975341796875 -0.021026611328125 -0.9250366210937526 +1.3245 -0.285308837890625 -0.021331787109375 -0.9250366210937526 +1.324625 -0.285308837890625 -0.021331787109375 -0.9250366210937526 +1.32475 -0.289581298828125 -0.02166748046875 -0.9250366210937526 +1.324875 -0.2938232421875 -0.02197265625 -0.9250366210937526 +1.325 -0.2938232421875 -0.02197265625 -0.9250366210937526 +1.325125 -0.298065185546875 -0.022308349609375 -0.9250366210937526 +1.32525 -0.298065185546875 -0.022308349609375 -0.9250366210937526 +1.325375 -0.30224609375 -0.022613525390625 -0.9250366210937526 +1.3255000000000002 -0.306396484375 -0.022918701171875 -0.9250366210937526 +1.325625 -0.306396484375 -0.022918701171875 -0.9250366210937526 +1.32575 -0.310516357421875 -0.023223876953125 -0.9250366210937526 +1.3258750000000002 -0.310516357421875 -0.023223876953125 -0.9250366210937526 +1.326 -0.314605712890625 -0.023529052734375 -0.9250366210937526 +1.326125 -0.31866455078125 -0.023834228515625 -0.9250366210937526 +1.32625 -0.31866455078125 -0.023834228515625 -0.9250366210937526 +1.326375 -0.322662353515625 -0.024139404296875 -0.9250366210937526 +1.3265 -0.322662353515625 -0.024139404296875 -0.9250366210937526 +1.326625 -0.32666015625 -0.024444580078125 -0.9250366210937526 +1.32675 -0.330596923828125 -0.024749755859375 -0.9250366210937526 +1.326875 -0.330596923828125 -0.024749755859375 -0.9250366210937526 +1.327 -0.334503173828125 -0.0250244140625 -0.9250366210937526 +1.327125 -0.334503173828125 -0.0250244140625 -0.9250366210937526 +1.32725 -0.33837890625 -0.02532958984375 -0.9250366210937526 +1.327375 -0.34222412109375 -0.025604248046875 -0.9250366210937526 +1.3275000000000002 -0.34222412109375 -0.025604248046875 -0.9250366210937526 +1.327625 -0.34600830078125 -0.02587890625 -0.9250366210937526 +1.32775 -0.34600830078125 -0.02587890625 -0.9250366210937526 +1.3278750000000002 -0.349761962890625 -0.02618408203125 -0.9250366210937526 +1.328 -0.353485107421875 -0.026458740234375 -0.9250366210937526 +1.328125 -0.353485107421875 -0.026458740234375 -0.9250366210937526 +1.32825 -0.357177734375 -0.0267333984375 -0.9250366210937526 +1.328375 -0.357177734375 -0.0267333984375 -0.9250366210937526 +1.3285 -0.360809326171875 -0.027008056640625 -0.9250366210937526 +1.328625 -0.36444091796875 -0.02728271484375 -0.9250366210937526 +1.32875 -0.36444091796875 -0.02728271484375 -0.9250366210937526 +1.328875 -0.36798095703125 -0.02752685546875 -0.9250366210937526 +1.329 -0.36798095703125 -0.02752685546875 -0.9250366210937526 +1.3291250000000002 -0.37152099609375 -0.027801513671875 -0.9250366210937526 +1.32925 -0.375 -0.028045654296875 -0.9250366210937526 +1.329375 -0.375 -0.028045654296875 -0.9250366210937526 +1.3295000000000002 -0.378448486328125 -0.0283203125 -0.9250366210937526 +1.329625 -0.378448486328125 -0.0283203125 -0.9250366210937526 +1.32975 -0.3818359375 -0.028564453125 -0.9250366210937526 +1.329875 -0.38519287109375 -0.02880859375 -0.9250366210937526 +1.33 -0.38519287109375 -0.02880859375 -0.9250366210937526 +1.330125 -0.388519287109375 -0.029083251953125 -0.9250366210937526 +1.33025 -0.388519287109375 -0.029083251953125 -0.9250366210937526 +1.330375 -0.39178466796875 -0.029327392578125 -0.9250366210937526 +1.3305 -0.39501953125 -0.029571533203125 -0.9250366210937526 +1.330625 -0.39501953125 -0.029571533203125 -0.9250366210937526 +1.33075 -0.398193359375 -0.02978515625 -0.9250366210937526 +1.330875 -0.398193359375 -0.02978515625 -0.9250366210937526 +1.331 -0.401336669921875 -0.030029296875 -0.9250366210937526 +1.3311250000000002 -0.404449462890625 -0.0302734375 -0.9250366210937526 +1.33125 -0.404449462890625 -0.0302734375 -0.9250366210937526 +1.331375 -0.407501220703125 -0.030487060546875 -0.9250366210937526 +1.3315000000000002 -0.407501220703125 -0.030487060546875 -0.9250366210937526 +1.331625 -0.4105224609375 -0.030731201171875 -0.9250366210937526 +1.33175 -0.413482666015625 -0.03094482421875 -0.9250366210937526 +1.331875 -0.413482666015625 -0.03094482421875 -0.9250366210937526 +1.332 -0.416412353515625 -0.031158447265625 -0.9250366210937526 +1.332125 -0.416412353515625 -0.031158447265625 -0.9250366210937526 +1.33225 -0.419281005859375 -0.0313720703125 -0.9250366210937526 +1.332375 -0.422119140625 -0.031585693359375 -0.9250366210937526 +1.3325 -0.422119140625 -0.031585693359375 -0.9250366210937526 +1.332625 -0.424896240234375 -0.03179931640625 -0.9250366210937526 +1.33275 -0.424896240234375 -0.03179931640625 -0.9250366210937526 +1.332875 -0.4276123046875 -0.032012939453125 -0.9250366210937526 +1.333 -0.4302978515625 -0.032196044921875 -0.9250366210937526 +1.3331250000000002 -0.4302978515625 -0.032196044921875 -0.9250366210937526 +1.33325 -0.432952880859375 -0.03240966796875 -0.9250366210937526 +1.333375 -0.432952880859375 -0.03240966796875 -0.9250366210937526 +1.3335000000000002 -0.435546875 -0.0325927734375 -0.9250366210937526 +1.333625 -0.438079833984375 -0.03277587890625 -0.9250366210937526 +1.33375 -0.438079833984375 -0.03277587890625 -0.9250366210937526 +1.333875 -0.440582275390625 -0.032958984375 -0.9250366210937526 +1.334 -0.440582275390625 -0.032958984375 -0.9250366210937526 +1.334125 -0.44305419921875 -0.03314208984375 -0.9250366210937526 +1.33425 -0.4454345703125 -0.0333251953125 -0.9250366210937526 +1.334375 -0.4454345703125 -0.0333251953125 -0.9250366210937526 +1.3345 -0.447784423828125 -0.03350830078125 -0.9250366210937526 +1.334625 -0.447784423828125 -0.03350830078125 -0.9250366210937526 +1.33475 -0.450103759765625 -0.03369140625 -0.9250366210937526 +1.334875 -0.452362060546875 -0.033843994140625 -0.9250366210937526 +1.335 -0.452362060546875 -0.033843994140625 -0.9250366210937526 +1.3351250000000002 -0.454559326171875 -0.034027099609375 -0.9250366210937526 +1.33525 -0.454559326171875 -0.034027099609375 -0.9250366210937526 +1.335375 -0.45672607421875 -0.0341796875 -0.9250366210937526 +1.3355000000000002 -0.458831787109375 -0.034332275390625 -0.9250366210937526 +1.335625 -0.458831787109375 -0.034332275390625 -0.9250366210937526 +1.33575 -0.46087646484375 -0.03448486328125 -0.9250366210937526 +1.335875 -0.46087646484375 -0.03448486328125 -0.9250366210937526 +1.336 -0.462890625 -0.034637451171875 -0.9250366210937526 +1.336125 -0.464813232421875 -0.0347900390625 -0.9250366210937526 +1.33625 -0.464813232421875 -0.0347900390625 -0.9250366210937526 +1.336375 -0.46673583984375 -0.034912109375 -0.9250366210937526 +1.3365 -0.46673583984375 -0.034912109375 -0.9250366210937526 +1.336625 -0.46856689453125 -0.035064697265625 -0.9250366210937526 +1.33675 -0.470367431640625 -0.035186767578125 -0.9250366210937526 +1.336875 -0.470367431640625 -0.035186767578125 -0.9250366210937526 +1.337 -0.472137451171875 -0.03533935546875 -0.9250366210937526 +1.3371250000000002 -0.472137451171875 -0.03533935546875 -0.9250366210937526 +1.33725 -0.47381591796875 -0.03546142578125 -0.9250366210937526 +1.337375 -0.4754638671875 -0.03558349609375 -0.9250366210937526 +1.3375000000000002 -0.4754638671875 -0.03558349609375 -0.9250366210937526 +1.337625 -0.47705078125 -0.03570556640625 -0.9250366210937526 +1.33775 -0.47705078125 -0.03570556640625 -0.9250366210937526 +1.337875 -0.478607177734375 -0.03582763671875 -0.9250366210937526 +1.338 -0.480072021484375 -0.035919189453125 -0.9250366210937526 +1.338125 -0.480072021484375 -0.035919189453125 -0.9250366210937526 +1.33825 -0.48150634765625 -0.036041259765625 -0.9250366210937526 +1.338375 -0.48150634765625 -0.036041259765625 -0.9250366210937526 +1.3385 -0.48291015625 -0.0361328125 -0.9250366210937526 +1.338625 -0.484222412109375 -0.036224365234375 -0.9250366210937526 +1.33875 -0.484222412109375 -0.036224365234375 -0.9250366210937526 +1.338875 -0.485504150390625 -0.036346435546875 -0.9250366210937526 +1.339 -0.485504150390625 -0.036346435546875 -0.9250366210937526 +1.3391250000000002 -0.486724853515625 -0.03643798828125 -0.9250366210937526 +1.33925 -0.487884521484375 -0.0364990234375 -0.9250366210937526 +1.339375 -0.487884521484375 -0.0364990234375 -0.9250366210937526 +1.3395000000000002 -0.489013671875 -0.036590576171875 -0.9250366210937526 +1.339625 -0.489013671875 -0.036590576171875 -0.9250366210937526 +1.33975 -0.490081787109375 -0.03668212890625 -0.9250366210937526 +1.339875 -0.4910888671875 -0.0367431640625 -0.9250366210937526 +1.34 -0.4910888671875 -0.0367431640625 -0.9250366210937526 +1.340125 -0.492034912109375 -0.036834716796875 -0.9250366210937526 +1.34025 -0.492034912109375 -0.036834716796875 -0.9250366210937526 +1.340375 -0.492950439453125 -0.036895751953125 -0.9250366210937526 +1.3405 -0.4937744140625 -0.036956787109375 -0.9250366210937526 +1.340625 -0.4937744140625 -0.036956787109375 -0.9250366210937526 +1.34075 -0.49456787109375 -0.037017822265625 -0.9250366210937526 +1.340875 -0.49456787109375 -0.037017822265625 -0.9250366210937526 +1.341 -0.49530029296875 -0.037078857421875 -0.9250366210937526 +1.3411250000000002 -0.496002197265625 -0.037109375 -0.9250366210937526 +1.34125 -0.496002197265625 -0.037109375 -0.9250366210937526 +1.341375 -0.496612548828125 -0.03717041015625 -0.9250366210937526 +1.3415000000000002 -0.496612548828125 -0.03717041015625 -0.9250366210937526 +1.341625 -0.4971923828125 -0.037200927734375 -0.9250366210937526 +1.34175 -0.497711181640625 -0.0372314453125 -0.9250366210937526 +1.341875 -0.497711181640625 -0.0372314453125 -0.9250366210937526 +1.342 -0.498199462890625 -0.03729248046875 -0.9250366210937526 +1.342125 -0.498199462890625 -0.03729248046875 -0.9250366210937526 +1.34225 -0.49859619140625 -0.037322998046875 -0.9250366210937526 +1.342375 -0.49896240234375 -0.037353515625 -0.9250366210937526 +1.3425 -0.49896240234375 -0.037353515625 -0.9250366210937526 +1.342625 -0.499267578125 -0.037353515625 -0.9250366210937526 +1.34275 -0.499267578125 -0.037353515625 -0.9250366210937526 +1.342875 -0.49951171875 -0.037384033203125 -0.9250366210937526 +1.343 -0.49969482421875 -0.037384033203125 -0.9250366210937526 +1.3431250000000002 -0.49969482421875 -0.037384033203125 -0.9250366210937526 +1.34325 -0.49981689453125 -0.03741455078125 -0.9250366210937526 +1.343375 -0.49981689453125 -0.03741455078125 -0.9250366210937526 +1.3435000000000002 -0.499908447265625 -0.03741455078125 -0.9250366210937526 +1.343625 -0.49993896484375 -0.03741455078125 -0.9250366210937526 +1.34375 -0.49993896484375 -0.03741455078125 -0.9250366210937526 +1.343875 -0.499908447265625 -0.03741455078125 -0.9250366210937526 +1.344 -0.499908447265625 0.0 -0.99987182617187488 +1.344125 -0.49981689453125 0.0 -0.99987182617187488 +1.34425 -0.49969482421875 0.0 -0.99987182617187488 +1.344375 -0.49969482421875 0.0 -0.99987182617187488 +1.3445 -0.49951171875 0.0 -0.99987182617187488 +1.344625 -0.49951171875 0.0 -0.99987182617187488 +1.3447500000000002 -0.499267578125 0.0 -0.99987182617187488 +1.344875 -0.49896240234375 0.0 -0.99987182617187488 +1.345 -0.49896240234375 0.0 -0.99987182617187488 +1.3451250000000002 -0.49859619140625 0.0 -0.99987182617187488 +1.34525 -0.49859619140625 0.0 -0.99987182617187488 +1.345375 -0.498199462890625 0.0 -0.99987182617187488 +1.3455 -0.497711181640625 0.0 -0.99987182617187488 +1.345625 -0.497711181640625 0.0 -0.99987182617187488 +1.34575 -0.4971923828125 0.0 -0.99987182617187488 +1.345875 -0.4971923828125 0.0 -0.99987182617187488 +1.346 -0.496612548828125 0.0 -0.99987182617187488 +1.346125 -0.496002197265625 0.0 -0.99987182617187488 +1.34625 -0.496002197265625 0.0 -0.99987182617187488 +1.346375 -0.49530029296875 0.0 -0.99987182617187488 +1.3465 -0.49530029296875 0.0 -0.99987182617187488 +1.346625 -0.49456787109375 0.0 -0.99987182617187488 +1.3467500000000002 -0.4937744140625 0.0 -0.99987182617187488 +1.346875 -0.4937744140625 0.0 -0.99987182617187488 +1.347 -0.492950439453125 0.0 -0.99987182617187488 +1.3471250000000002 -0.492950439453125 0.0 -0.99987182617187488 +1.34725 -0.492034912109375 0.0 -0.99987182617187488 +1.347375 -0.4910888671875 0.0 -0.99987182617187488 +1.3475 -0.4910888671875 0.0 -0.99987182617187488 +1.347625 -0.490081787109375 0.0 -0.99987182617187488 +1.34775 -0.490081787109375 0.0 -0.99987182617187488 +1.347875 -0.489013671875 0.0 -0.99987182617187488 +1.348 -0.487884521484375 0.0 -0.99987182617187488 +1.348125 -0.487884521484375 0.0 -0.99987182617187488 +1.34825 -0.486724853515625 0.0 -0.99987182617187488 +1.348375 -0.486724853515625 0.0 -0.99987182617187488 +1.3485 -0.485504150390625 0.0 -0.99987182617187488 +1.348625 -0.484222412109375 0.0 -0.99987182617187488 +1.3487500000000002 -0.484222412109375 0.0 -0.99987182617187488 +1.348875 -0.48291015625 0.0 -0.99987182617187488 +1.349 -0.48291015625 0.0 -0.99987182617187488 +1.3491250000000002 -0.48150634765625 0.0 -0.99987182617187488 +1.34925 -0.480072021484375 0.0 -0.99987182617187488 +1.349375 -0.480072021484375 0.0 -0.99987182617187488 +1.3495 -0.478607177734375 0.0 -0.99987182617187488 +1.349625 -0.478607177734375 0.0 -0.99987182617187488 +1.34975 -0.47705078125 0.0 -0.99987182617187488 +1.349875 -0.4754638671875 0.0 -0.99987182617187488 +1.35 -0.4754638671875 0.0 -0.99987182617187488 +1.350125 -0.47381591796875 0.0 -0.99987182617187488 +1.35025 -0.47381591796875 0.0 -0.99987182617187488 +1.350375 -0.472137451171875 0.0 -0.99987182617187488 +1.3505 -0.470367431640625 0.0 -0.99987182617187488 +1.350625 -0.470367431640625 0.0 -0.99987182617187488 +1.3507500000000002 -0.46856689453125 0.0 -0.99987182617187488 +1.350875 -0.46856689453125 0.0 -0.99987182617187488 +1.351 -0.46673583984375 0.0 -0.99987182617187488 +1.3511250000000002 -0.464813232421875 0.0 -0.99987182617187488 +1.35125 -0.464813232421875 0.0 -0.99987182617187488 +1.351375 -0.462890625 0.0 -0.99987182617187488 +1.3515 -0.462890625 0.0 -0.99987182617187488 +1.351625 -0.46087646484375 0.0 -0.99987182617187488 +1.35175 -0.458831787109375 0.0 -0.99987182617187488 +1.351875 -0.458831787109375 0.0 -0.99987182617187488 +1.352 -0.45672607421875 0.0 -0.99987182617187488 +1.352125 -0.45672607421875 0.0 -0.99987182617187488 +1.35225 -0.454559326171875 0.0 -0.99987182617187488 +1.352375 -0.452362060546875 0.0 -0.99987182617187488 +1.3525 -0.452362060546875 0.0 -0.99987182617187488 +1.352625 -0.450103759765625 0.0 -0.99987182617187488 +1.3527500000000002 -0.450103759765625 0.0 -0.99987182617187488 +1.352875 -0.447784423828125 0.0 -0.99987182617187488 +1.353 -0.4454345703125 0.0 -0.99987182617187488 +1.3531250000000002 -0.4454345703125 0.0 -0.99987182617187488 +1.35325 -0.44305419921875 0.0 -0.99987182617187488 +1.353375 -0.44305419921875 0.0 -0.99987182617187488 +1.3535 -0.440582275390625 0.0 -0.99987182617187488 +1.353625 -0.438079833984375 0.0 -0.99987182617187488 +1.35375 -0.438079833984375 0.0 -0.99987182617187488 +1.353875 -0.435546875 0.0 -0.99987182617187488 +1.354 -0.435546875 0.0 -0.99987182617187488 +1.354125 -0.432952880859375 0.0 -0.99987182617187488 +1.35425 -0.4302978515625 0.0 -0.99987182617187488 +1.354375 -0.4302978515625 0.0 -0.99987182617187488 +1.3545 -0.4276123046875 0.0 -0.99987182617187488 +1.354625 -0.4276123046875 0.0 -0.99987182617187488 +1.3547500000000002 -0.424896240234375 0.0 -0.99987182617187488 +1.354875 -0.422119140625 0.0 -0.99987182617187488 +1.355 -0.422119140625 0.0 -0.99987182617187488 +1.3551250000000002 -0.419281005859375 0.0 -0.99987182617187488 +1.35525 -0.419281005859375 0.0 -0.99987182617187488 +1.355375 -0.416412353515625 0.0 -0.99987182617187488 +1.3555 -0.413482666015625 0.0 -0.99987182617187488 +1.355625 -0.413482666015625 0.0 -0.99987182617187488 +1.35575 -0.4105224609375 0.0 -0.99987182617187488 +1.355875 -0.4105224609375 0.0 -0.99987182617187488 +1.356 -0.407501220703125 0.0 -0.99987182617187488 +1.356125 -0.404449462890625 0.0 -0.99987182617187488 +1.35625 -0.404449462890625 0.0 -0.99987182617187488 +1.356375 -0.401336669921875 0.0 -0.99987182617187488 +1.3565 -0.401336669921875 0.0 -0.99987182617187488 +1.356625 -0.398193359375 0.0 -0.99987182617187488 +1.3567500000000002 -0.39501953125 0.0 -0.99987182617187488 +1.356875 -0.39501953125 0.0 -0.99987182617187488 +1.357 -0.39178466796875 0.0 -0.99987182617187488 +1.3571250000000002 -0.39178466796875 0.0 -0.99987182617187488 +1.35725 -0.388519287109375 0.0 -0.99987182617187488 +1.357375 -0.38519287109375 0.0 -0.99987182617187488 +1.3575 -0.38519287109375 0.0 -0.99987182617187488 +1.357625 -0.3818359375 0.0 -0.99987182617187488 +1.35775 -0.3818359375 0.0 -0.99987182617187488 +1.357875 -0.378448486328125 0.0 -0.99987182617187488 +1.358 -0.375 0.0 -0.99987182617187488 +1.358125 -0.375 0.0 -0.99987182617187488 +1.35825 -0.37152099609375 0.0 -0.99987182617187488 +1.358375 -0.37152099609375 0.0 -0.99987182617187488 +1.3585 -0.36798095703125 0.0 -0.99987182617187488 +1.358625 -0.36444091796875 0.0 -0.99987182617187488 +1.3587500000000002 -0.36444091796875 0.0 -0.99987182617187488 +1.358875 -0.360809326171875 0.0 -0.99987182617187488 +1.359 -0.360809326171875 0.0 -0.99987182617187488 +1.3591250000000002 -0.357177734375 0.0 -0.99987182617187488 +1.35925 -0.353485107421875 0.0 -0.99987182617187488 +1.359375 -0.353485107421875 0.0 -0.99987182617187488 +1.3595 -0.349761962890625 0.0 -0.99987182617187488 +1.359625 -0.349761962890625 0.0 -0.99987182617187488 +1.35975 -0.34600830078125 0.0 -0.99987182617187488 +1.359875 -0.34222412109375 0.0 -0.99987182617187488 +1.36 -0.34222412109375 0.0 -0.99987182617187488 +1.360125 -0.33837890625 0.0 -0.99987182617187488 +1.36025 -0.33837890625 0.0 -0.99987182617187488 +1.3603750000000002 -0.334503173828125 0.0 -0.99987182617187488 +1.3605 -0.330596923828125 0.0 -0.99987182617187488 +1.360625 -0.330596923828125 0.0 -0.99987182617187488 +1.3607500000000002 -0.32666015625 0.0 -0.99987182617187488 +1.360875 -0.32666015625 0.0 -0.99987182617187488 +1.361 -0.322662353515625 0.0 -0.99987182617187488 +1.361125 -0.31866455078125 0.0 -0.99987182617187488 +1.36125 -0.31866455078125 0.0 -0.99987182617187488 +1.361375 -0.314605712890625 0.0 -0.99987182617187488 +1.3615 -0.314605712890625 0.0 -0.99987182617187488 +1.361625 -0.310516357421875 0.0 -0.99987182617187488 +1.36175 -0.306396484375 0.0 -0.99987182617187488 +1.361875 -0.306396484375 0.0 -0.99987182617187488 +1.362 -0.30224609375 0.0 -0.99987182617187488 +1.362125 -0.30224609375 0.0 -0.99987182617187488 +1.36225 -0.298065185546875 0.0 -0.99987182617187488 +1.3623750000000002 -0.2938232421875 0.0 -0.99987182617187488 +1.3625 -0.2938232421875 0.0 -0.99987182617187488 +1.362625 -0.289581298828125 0.0 -0.99987182617187488 +1.3627500000000002 -0.289581298828125 0.0 -0.99987182617187488 +1.362875 -0.285308837890625 0.0 -0.99987182617187488 +1.363 -0.280975341796875 0.0 -0.99987182617187488 +1.363125 -0.280975341796875 0.0 -0.99987182617187488 +1.36325 -0.276641845703125 0.0 -0.99987182617187488 +1.363375 -0.276641845703125 0.0 -0.99987182617187488 +1.3635 -0.27227783203125 0.0 -0.99987182617187488 +1.363625 -0.267852783203125 0.0 -0.99987182617187488 +1.36375 -0.267852783203125 0.0 -0.99987182617187488 +1.363875 -0.263427734375 0.0 -0.99987182617187488 +1.364 -0.263427734375 0.0 -0.99987182617187488 +1.364125 -0.25897216796875 0.0 -0.99987182617187488 +1.36425 -0.25445556640625 0.0 -0.99987182617187488 +1.3643750000000002 -0.25445556640625 0.0 -0.99987182617187488 +1.3645 -0.249969482421875 0.0 -0.99987182617187488 +1.364625 -0.249969482421875 0.0 -0.99987182617187488 +1.3647500000000002 -0.24542236328125 0.0 -0.99987182617187488 +1.364875 -0.2408447265625 0.0 -0.99987182617187488 +1.365 -0.2408447265625 0.0 -0.99987182617187488 +1.365125 -0.236236572265625 0.0 -0.99987182617187488 +1.36525 -0.236236572265625 0.0 -0.99987182617187488 +1.365375 -0.231597900390625 0.0 -0.99987182617187488 +1.3655 -0.226959228515625 0.0 -0.99987182617187488 +1.365625 -0.226959228515625 0.0 -0.99987182617187488 +1.36575 -0.2222900390625 0.0 -0.99987182617187488 +1.365875 -0.2222900390625 0.0 -0.99987182617187488 +1.366 -0.21759033203125 0.0 -0.99987182617187488 +1.366125 -0.212860107421875 0.0 -0.99987182617187488 +1.36625 -0.212860107421875 0.0 -0.99987182617187488 +1.3663750000000002 -0.208099365234375 0.0 -0.99987182617187488 +1.3665 -0.208099365234375 0.0 -0.99987182617187488 +1.366625 -0.203338623046875 0.0 -0.99987182617187488 +1.3667500000000002 -0.19854736328125 0.0 -0.99987182617187488 +1.366875 -0.19854736328125 0.0 -0.99987182617187488 +1.367 -0.1937255859375 0.0 -0.99987182617187488 +1.367125 -0.1937255859375 0.0 -0.99987182617187488 +1.36725 -0.188873291015625 0.0 -0.99987182617187488 +1.367375 -0.18402099609375 0.0 -0.99987182617187488 +1.3675 -0.18402099609375 0.0 -0.99987182617187488 +1.367625 -0.17913818359375 0.0 -0.99987182617187488 +1.36775 -0.17913818359375 0.0 -0.99987182617187488 +1.367875 -0.17425537109375 0.0 -0.99987182617187488 +1.368 -0.169342041015625 0.0 -0.99987182617187488 +1.368125 -0.169342041015625 0.0 -0.99987182617187488 +1.36825 -0.164398193359375 0.0 -0.99987182617187488 +1.3683750000000002 -0.164398193359375 0.0 -0.99987182617187488 +1.3685 -0.159454345703125 0.0 -0.99987182617187488 +1.368625 -0.15447998046875 0.0 -0.99987182617187488 +1.3687500000000002 -0.15447998046875 0.0 -0.99987182617187488 +1.368875 -0.14947509765625 0.0 -0.99987182617187488 +1.369 -0.14947509765625 0.0 -0.99987182617187488 +1.369125 -0.14447021484375 0.0 -0.99987182617187488 +1.36925 -0.13946533203125 0.0 -0.99987182617187488 +1.369375 -0.13946533203125 0.0 -0.99987182617187488 +1.3695 -0.134429931640625 0.0 -0.99987182617187488 +1.369625 -0.134429931640625 0.0 -0.99987182617187488 +1.36975 -0.129364013671875 0.0 -0.99987182617187488 +1.369875 -0.124298095703125 0.0 -0.99987182617187488 +1.37 -0.124298095703125 0.0 -0.99987182617187488 +1.370125 -0.119232177734375 0.0 -0.99987182617187488 +1.37025 -0.119232177734375 0.0 -0.99987182617187488 +1.3703750000000002 -0.1141357421875 0.0 -0.99987182617187488 +1.3705 -0.109039306640625 0.0 -0.99987182617187488 +1.370625 -0.109039306640625 0.0 -0.99987182617187488 +1.3707500000000002 -0.103912353515625 0.0 -0.99987182617187488 +1.370875 -0.103912353515625 0.0 -0.99987182617187488 +1.371 -0.098785400390625 0.0 -0.99987182617187488 +1.371125 -0.093658447265625 0.0 -0.99987182617187488 +1.37125 -0.093658447265625 0.0 -0.99987182617187488 +1.371375 -0.0885009765625 0.0 -0.99987182617187488 +1.3715 -0.0885009765625 0.0 -0.99987182617187488 +1.371625 -0.083343505859375 0.0 -0.99987182617187488 +1.37175 -0.07818603515625 0.0 -0.99987182617187488 +1.371875 -0.07818603515625 0.0 -0.99987182617187488 +1.372 -0.072998046875 0.0 -0.99987182617187488 +1.372125 -0.072998046875 0.0 -0.99987182617187488 +1.37225 -0.06781005859375 0.0 -0.99987182617187488 +1.3723750000000002 -0.0626220703125 0.0 -0.99987182617187488 +1.3725 -0.0626220703125 0.0 -0.99987182617187488 +1.372625 -0.05743408203125 0.0 -0.99987182617187488 +1.3727500000000002 -0.05743408203125 0.0 -0.99987182617187488 +1.372875 -0.05224609375 0.0 -0.99987182617187488 +1.373 -0.047027587890625 0.0 -0.99987182617187488 +1.373125 -0.047027587890625 0.0 -0.99987182617187488 +1.37325 -0.04180908203125 0.0 -0.99987182617187488 +1.373375 -0.04180908203125 0.0 -0.99987182617187488 +1.3735 -0.036590576171875 0.0 -0.99987182617187488 +1.373625 -0.0313720703125 0.0 -0.99987182617187488 +1.37375 -0.0313720703125 0.0 -0.99987182617187488 +1.373875 -0.026123046875 0.0 -0.99987182617187488 +1.374 -0.026123046875 0.0 -0.99987182617187488 +1.374125 -0.020904541015625 0.0 -0.99987182617187488 +1.37425 -0.01568603515625 0.0 -0.99987182617187488 +1.3743750000000002 -0.01568603515625 0.0 -0.99987182617187488 +1.3745 -0.01043701171875 0.0 -0.99987182617187488 +1.374625 -0.01043701171875 0.0 -0.99987182617187488 +1.3747500000000002 -0.005218505859375 0.0 -0.99987182617187488 +1.374875 0.0 0.0 -0.99987182617187488 +1.375 0.0 0.0 -0.99987182617187488 +1.375125 0.005218505859375 0.0 -0.99987182617187488 +1.37525 0.005218505859375 0.0 -0.99987182617187488 +1.375375 0.01043701171875 0.0 -0.99987182617187488 +1.3755 0.01568603515625 0.0 -0.99987182617187488 +1.375625 0.01568603515625 0.0 -0.99987182617187488 +1.37575 0.020904541015625 0.0 -0.99987182617187488 +1.375875 0.020904541015625 0.0 -0.99987182617187488 +1.3760000000000002 0.026123046875 0.002197265625 -0.915197753906247 +1.376125 0.0313720703125 0.002655029296875 -0.915197753906247 +1.37625 0.0313720703125 0.002655029296875 -0.915197753906247 +1.3763750000000002 0.036590576171875 0.003082275390625 -0.915197753906247 +1.3765 0.036590576171875 0.003082275390625 -0.915197753906247 +1.376625 0.04180908203125 0.0035400390625 -0.915197753906247 +1.37675 0.047027587890625 0.00396728515625 -0.915197753906247 +1.376875 0.047027587890625 0.00396728515625 -0.915197753906247 +1.377 0.05224609375 0.004425048828125 -0.915197753906247 +1.377125 0.05224609375 0.004425048828125 -0.915197753906247 +1.37725 0.05743408203125 0.004852294921875 -0.915197753906247 +1.377375 0.0626220703125 0.005279541015625 -0.915197753906247 +1.3775 0.0626220703125 0.005279541015625 -0.915197753906247 +1.377625 0.06781005859375 0.0057373046875 -0.915197753906247 +1.37775 0.06781005859375 0.0057373046875 -0.915197753906247 +1.377875 0.072998046875 0.00616455078125 -0.915197753906247 +1.3780000000000002 0.07818603515625 0.006622314453125 -0.915197753906247 +1.378125 0.07818603515625 0.006622314453125 -0.915197753906247 +1.37825 0.083343505859375 0.007049560546875 -0.915197753906247 +1.3783750000000002 0.083343505859375 0.007049560546875 -0.915197753906247 +1.3785 0.0885009765625 0.007476806640625 -0.915197753906247 +1.378625 0.093658447265625 0.0079345703125 -0.915197753906247 +1.37875 0.093658447265625 0.0079345703125 -0.915197753906247 +1.378875 0.098785400390625 0.00836181640625 -0.915197753906247 +1.379 0.098785400390625 0.00836181640625 -0.915197753906247 +1.379125 0.103912353515625 0.0087890625 -0.915197753906247 +1.37925 0.109039306640625 0.00921630859375 -0.915197753906247 +1.379375 0.109039306640625 0.00921630859375 -0.915197753906247 +1.3795 0.1141357421875 0.0096435546875 -0.915197753906247 +1.379625 0.1141357421875 0.0096435546875 -0.915197753906247 +1.37975 0.119232177734375 0.010101318359375 -0.915197753906247 +1.379875 0.124298095703125 0.010528564453125 -0.915197753906247 +1.3800000000000002 0.124298095703125 0.010528564453125 -0.915197753906247 +1.380125 0.129364013671875 0.010955810546875 -0.915197753906247 +1.38025 0.129364013671875 0.010955810546875 -0.915197753906247 +1.3803750000000002 0.134429931640625 0.011383056640625 -0.915197753906247 +1.3805 0.13946533203125 0.011810302734375 -0.915197753906247 +1.380625 0.13946533203125 0.011810302734375 -0.915197753906247 +1.38075 0.14447021484375 0.012237548828125 -0.915197753906247 +1.380875 0.14447021484375 0.012237548828125 -0.915197753906247 +1.381 0.14947509765625 0.012664794921875 -0.915197753906247 +1.381125 0.15447998046875 0.0130615234375 -0.915197753906247 +1.38125 0.15447998046875 0.0130615234375 -0.915197753906247 +1.381375 0.159454345703125 0.01348876953125 -0.915197753906247 +1.3815 0.159454345703125 0.01348876953125 -0.915197753906247 +1.381625 0.164398193359375 0.013916015625 -0.915197753906247 +1.38175 0.169342041015625 0.01434326171875 -0.915197753906247 +1.381875 0.169342041015625 0.01434326171875 -0.915197753906247 +1.3820000000000002 0.17425537109375 0.014739990234375 -0.915197753906247 +1.382125 0.17425537109375 0.014739990234375 -0.915197753906247 +1.38225 0.17913818359375 0.015167236328125 -0.915197753906247 +1.3823750000000002 0.18402099609375 0.01556396484375 -0.915197753906247 +1.3825 0.18402099609375 0.01556396484375 -0.915197753906247 +1.382625 0.188873291015625 0.0159912109375 -0.915197753906247 +1.38275 0.188873291015625 0.0159912109375 -0.915197753906247 +1.382875 0.1937255859375 0.016387939453125 -0.915197753906247 +1.383 0.19854736328125 0.016815185546875 -0.915197753906247 +1.383125 0.19854736328125 0.016815185546875 -0.915197753906247 +1.38325 0.203338623046875 0.0172119140625 -0.915197753906247 +1.383375 0.203338623046875 0.0172119140625 -0.915197753906247 +1.3835 0.208099365234375 0.017608642578125 -0.915197753906247 +1.383625 0.212860107421875 0.01800537109375 -0.915197753906247 +1.38375 0.212860107421875 0.01800537109375 -0.915197753906247 +1.383875 0.21759033203125 0.0184326171875 -0.915197753906247 +1.3840000000000002 0.21759033203125 0.0184326171875 -0.915197753906247 +1.384125 0.2222900390625 0.018829345703125 -0.915197753906247 +1.38425 0.226959228515625 0.01922607421875 -0.915197753906247 +1.3843750000000002 0.226959228515625 0.01922607421875 -0.915197753906247 +1.3845 0.231597900390625 0.019622802734375 -0.915197753906247 +1.384625 0.231597900390625 0.019622802734375 -0.915197753906247 +1.38475 0.236236572265625 0.019989013671875 -0.915197753906247 +1.384875 0.2408447265625 0.0203857421875 -0.915197753906247 +1.385 0.2408447265625 0.0203857421875 -0.915197753906247 +1.385125 0.24542236328125 0.020782470703125 -0.915197753906247 +1.38525 0.24542236328125 0.020782470703125 -0.915197753906247 +1.385375 0.249969482421875 0.021148681640625 -0.915197753906247 +1.3855 0.25445556640625 0.02154541015625 -0.915197753906247 +1.385625 0.25445556640625 0.02154541015625 -0.915197753906247 +1.38575 0.25897216796875 0.02191162109375 -0.915197753906247 +1.385875 0.25897216796875 0.02191162109375 -0.915197753906247 +1.3860000000000002 0.263427734375 0.022308349609375 -0.915197753906247 +1.386125 0.267852783203125 0.022674560546875 -0.915197753906247 +1.38625 0.267852783203125 0.022674560546875 -0.915197753906247 +1.3863750000000002 0.27227783203125 0.023040771484375 -0.915197753906247 +1.3865 0.27227783203125 0.023040771484375 -0.915197753906247 +1.386625 0.276641845703125 0.0234375 -0.915197753906247 +1.38675 0.280975341796875 0.0238037109375 -0.915197753906247 +1.386875 0.280975341796875 0.0238037109375 -0.915197753906247 +1.387 0.285308837890625 0.024169921875 -0.915197753906247 +1.387125 0.285308837890625 0.024169921875 -0.915197753906247 +1.38725 0.289581298828125 0.0245361328125 -0.915197753906247 +1.387375 0.2938232421875 0.024871826171875 -0.915197753906247 +1.3875 0.2938232421875 0.024871826171875 -0.915197753906247 +1.387625 0.298065185546875 0.025238037109375 -0.915197753906247 +1.38775 0.298065185546875 0.025238037109375 -0.915197753906247 +1.387875 0.30224609375 0.025604248046875 -0.915197753906247 +1.3880000000000002 0.306396484375 0.02593994140625 -0.915197753906247 +1.388125 0.306396484375 0.02593994140625 -0.915197753906247 +1.38825 0.310516357421875 0.02630615234375 -0.915197753906247 +1.3883750000000002 0.310516357421875 0.02630615234375 -0.915197753906247 +1.3885 0.314605712890625 0.026641845703125 -0.915197753906247 +1.388625 0.31866455078125 0.0269775390625 -0.915197753906247 +1.38875 0.31866455078125 0.0269775390625 -0.915197753906247 +1.388875 0.322662353515625 0.027313232421875 -0.915197753906247 +1.389 0.322662353515625 0.027313232421875 -0.915197753906247 +1.389125 0.32666015625 0.02764892578125 -0.915197753906247 +1.38925 0.330596923828125 0.027984619140625 -0.915197753906247 +1.389375 0.330596923828125 0.027984619140625 -0.915197753906247 +1.3895 0.334503173828125 0.0283203125 -0.915197753906247 +1.389625 0.334503173828125 0.0283203125 -0.915197753906247 +1.38975 0.33837890625 0.028656005859375 -0.915197753906247 +1.389875 0.34222412109375 0.02899169921875 -0.915197753906247 +1.3900000000000002 0.34222412109375 0.02899169921875 -0.915197753906247 +1.390125 0.34600830078125 0.029296875 -0.915197753906247 +1.39025 0.34600830078125 0.029296875 -0.915197753906247 +1.3903750000000002 0.349761962890625 0.029632568359375 -0.915197753906247 +1.3905 0.353485107421875 0.029937744140625 -0.915197753906247 +1.390625 0.353485107421875 0.029937744140625 -0.915197753906247 +1.39075 0.357177734375 0.030242919921875 -0.915197753906247 +1.390875 0.357177734375 0.030242919921875 -0.915197753906247 +1.391 0.360809326171875 0.030548095703125 -0.915197753906247 +1.391125 0.36444091796875 0.030853271484375 -0.915197753906247 +1.39125 0.36444091796875 0.030853271484375 -0.915197753906247 +1.391375 0.36798095703125 0.031158447265625 -0.915197753906247 +1.3915 0.36798095703125 0.031158447265625 -0.915197753906247 +1.3916250000000002 0.37152099609375 0.031463623046875 -0.915197753906247 +1.39175 0.375 0.031768798828125 -0.915197753906247 +1.391875 0.375 0.031768798828125 -0.915197753906247 +1.3920000000000002 0.378448486328125 0.03204345703125 -0.915197753906247 +1.392125 0.378448486328125 0.03204345703125 -0.915197753906247 +1.39225 0.3818359375 0.0323486328125 -0.915197753906247 +1.392375 0.38519287109375 0.032623291015625 -0.915197753906247 +1.3925 0.38519287109375 0.032623291015625 -0.915197753906247 +1.392625 0.388519287109375 0.03289794921875 -0.915197753906247 +1.39275 0.388519287109375 0.03289794921875 -0.915197753906247 +1.392875 0.39178466796875 0.033172607421875 -0.915197753906247 +1.393 0.39501953125 0.033447265625 -0.915197753906247 +1.393125 0.39501953125 0.033447265625 -0.915197753906247 +1.39325 0.398193359375 0.033721923828125 -0.915197753906247 +1.393375 0.398193359375 0.033721923828125 -0.915197753906247 +1.3935 0.401336669921875 0.03399658203125 -0.915197753906247 +1.3936250000000002 0.404449462890625 0.03424072265625 -0.915197753906247 +1.39375 0.404449462890625 0.03424072265625 -0.915197753906247 +1.393875 0.407501220703125 0.034515380859375 -0.915197753906247 +1.3940000000000002 0.407501220703125 0.034515380859375 -0.915197753906247 +1.394125 0.4105224609375 0.034759521484375 -0.915197753906247 +1.39425 0.413482666015625 0.035003662109375 -0.915197753906247 +1.394375 0.413482666015625 0.035003662109375 -0.915197753906247 +1.3945 0.416412353515625 0.0352783203125 -0.915197753906247 +1.394625 0.416412353515625 0.0352783203125 -0.915197753906247 +1.39475 0.419281005859375 0.0355224609375 -0.915197753906247 +1.394875 0.422119140625 0.035736083984375 -0.915197753906247 +1.395 0.422119140625 0.035736083984375 -0.915197753906247 +1.395125 0.424896240234375 0.035980224609375 -0.915197753906247 +1.39525 0.424896240234375 0.035980224609375 -0.915197753906247 +1.395375 0.4276123046875 0.036224365234375 -0.915197753906247 +1.3955 0.4302978515625 0.03643798828125 -0.915197753906247 +1.3956250000000002 0.4302978515625 0.03643798828125 -0.915197753906247 +1.39575 0.432952880859375 0.03668212890625 -0.915197753906247 +1.395875 0.432952880859375 0.03668212890625 -0.915197753906247 +1.3960000000000002 0.435546875 0.036895751953125 -0.915197753906247 +1.396125 0.438079833984375 0.037109375 -0.915197753906247 +1.39625 0.438079833984375 0.037109375 -0.915197753906247 +1.396375 0.440582275390625 0.037322998046875 -0.915197753906247 +1.3965 0.440582275390625 0.037322998046875 -0.915197753906247 +1.396625 0.44305419921875 0.03753662109375 -0.915197753906247 +1.39675 0.4454345703125 0.0377197265625 -0.915197753906247 +1.396875 0.4454345703125 0.0377197265625 -0.915197753906247 +1.397 0.447784423828125 0.037933349609375 -0.915197753906247 +1.397125 0.447784423828125 0.037933349609375 -0.915197753906247 +1.39725 0.450103759765625 0.038116455078125 -0.915197753906247 +1.397375 0.452362060546875 0.038299560546875 -0.915197753906247 +1.3975 0.452362060546875 0.038299560546875 -0.915197753906247 +1.3976250000000002 0.454559326171875 0.038482666015625 -0.915197753906247 +1.39775 0.454559326171875 0.038482666015625 -0.915197753906247 +1.397875 0.45672607421875 0.038665771484375 -0.915197753906247 +1.3980000000000002 0.458831787109375 0.038848876953125 -0.915197753906247 +1.398125 0.458831787109375 0.038848876953125 -0.915197753906247 +1.39825 0.46087646484375 0.039031982421875 -0.915197753906247 +1.398375 0.46087646484375 0.039031982421875 -0.915197753906247 +1.3985 0.462890625 0.039215087890625 -0.915197753906247 +1.398625 0.464813232421875 0.03936767578125 -0.915197753906247 +1.39875 0.464813232421875 0.03936767578125 -0.915197753906247 +1.398875 0.46673583984375 0.039520263671875 -0.915197753906247 +1.399 0.46673583984375 0.039520263671875 -0.915197753906247 +1.399125 0.46856689453125 0.0396728515625 -0.915197753906247 +1.39925 0.470367431640625 0.039825439453125 -0.915197753906247 +1.399375 0.470367431640625 0.039825439453125 -0.915197753906247 +1.3995 0.472137451171875 0.03997802734375 -0.915197753906247 +1.3996250000000002 0.472137451171875 0.03997802734375 -0.915197753906247 +1.39975 0.47381591796875 0.040130615234375 -0.915197753906247 +1.399875 0.4754638671875 0.040283203125 -0.915197753906247 +1.4000000000000002 0.4754638671875 0.040283203125 -0.915197753906247 +1.400125 0.47705078125 0.0404052734375 -0.915197753906247 +1.40025 0.47705078125 0.0404052734375 -0.915197753906247 +1.400375 0.478607177734375 0.04052734375 -0.915197753906247 +1.4005 0.480072021484375 0.0406494140625 -0.915197753906247 +1.400625 0.480072021484375 0.0406494140625 -0.915197753906247 +1.40075 0.48150634765625 0.040771484375 -0.915197753906247 +1.400875 0.48150634765625 0.040771484375 -0.915197753906247 +1.401 0.48291015625 0.0408935546875 -0.915197753906247 +1.401125 0.484222412109375 0.041015625 -0.915197753906247 +1.40125 0.484222412109375 0.041015625 -0.915197753906247 +1.401375 0.485504150390625 0.041107177734375 -0.915197753906247 +1.4015 0.485504150390625 0.041107177734375 -0.915197753906247 +1.4016250000000002 0.486724853515625 0.041229248046875 -0.915197753906247 +1.40175 0.487884521484375 0.04132080078125 -0.915197753906247 +1.401875 0.487884521484375 0.04132080078125 -0.915197753906247 +1.4020000000000002 0.489013671875 0.041412353515625 -0.915197753906247 +1.402125 0.489013671875 0.041412353515625 -0.915197753906247 +1.40225 0.490081787109375 0.04150390625 -0.915197753906247 +1.402375 0.4910888671875 0.041595458984375 -0.915197753906247 +1.4025 0.4910888671875 0.041595458984375 -0.915197753906247 +1.402625 0.492034912109375 0.04168701171875 -0.915197753906247 +1.40275 0.492034912109375 0.04168701171875 -0.915197753906247 +1.402875 0.492950439453125 0.041748046875 -0.915197753906247 +1.403 0.4937744140625 0.04180908203125 -0.915197753906247 +1.403125 0.4937744140625 0.04180908203125 -0.915197753906247 +1.40325 0.49456787109375 0.041900634765625 -0.915197753906247 +1.403375 0.49456787109375 0.041900634765625 -0.915197753906247 +1.4035 0.49530029296875 0.041961669921875 -0.915197753906247 +1.4036250000000002 0.496002197265625 0.0419921875 -0.915197753906247 +1.40375 0.496002197265625 0.0419921875 -0.915197753906247 +1.403875 0.496612548828125 0.04205322265625 -0.915197753906247 +1.4040000000000002 0.496612548828125 0.04205322265625 -0.915197753906247 +1.404125 0.4971923828125 0.0421142578125 -0.915197753906247 +1.40425 0.497711181640625 0.042144775390625 -0.915197753906247 +1.404375 0.497711181640625 0.042144775390625 -0.915197753906247 +1.4045 0.498199462890625 0.042205810546875 -0.915197753906247 +1.404625 0.498199462890625 0.042205810546875 -0.915197753906247 +1.40475 0.49859619140625 0.042236328125 -0.915197753906247 +1.404875 0.49896240234375 0.042266845703125 -0.915197753906247 +1.405 0.49896240234375 0.042266845703125 -0.915197753906247 +1.405125 0.499267578125 0.04229736328125 -0.915197753906247 +1.40525 0.499267578125 0.04229736328125 -0.915197753906247 +1.405375 0.49951171875 0.04229736328125 -0.915197753906247 +1.4055 0.49969482421875 0.042327880859375 -0.915197753906247 +1.4056250000000002 0.49969482421875 0.042327880859375 -0.915197753906247 +1.40575 0.49981689453125 0.042327880859375 -0.915197753906247 +1.405875 0.49981689453125 0.042327880859375 -0.915197753906247 +1.4060000000000002 0.499908447265625 0.042327880859375 -0.915197753906247 +1.406125 0.49993896484375 0.042327880859375 -0.915197753906247 +1.40625 0.49993896484375 0.042327880859375 -0.915197753906247 +1.406375 0.499908447265625 0.042327880859375 -0.915197753906247 +1.4065 0.499908447265625 0.042327880859375 -0.915197753906247 +1.406625 0.49981689453125 0.042327880859375 -0.915197753906247 +1.40675 0.49969482421875 0.042327880859375 -0.915197753906247 +1.406875 0.49969482421875 0.042327880859375 -0.915197753906247 +1.407 0.49951171875 0.04229736328125 -0.915197753906247 +1.407125 0.49951171875 0.04229736328125 -0.915197753906247 +1.4072500000000002 0.499267578125 0.04229736328125 -0.915197753906247 +1.407375 0.49896240234375 0.042266845703125 -0.915197753906247 +1.4075 0.49896240234375 0.042266845703125 -0.915197753906247 +1.4076250000000002 0.49859619140625 0.042236328125 -0.915197753906247 +1.40775 0.49859619140625 0.042236328125 -0.915197753906247 +1.407875 0.498199462890625 0.042205810546875 -0.915197753906247 +1.408 0.497711181640625 0.156982421875 -0.6845092773437448 +1.408125 0.497711181640625 0.156982421875 -0.6845092773437448 +1.40825 0.4971923828125 0.15679931640625 -0.6845092773437448 +1.408375 0.4971923828125 0.15679931640625 -0.6845092773437448 +1.4085 0.496612548828125 0.1566162109375 -0.6845092773437448 +1.408625 0.496002197265625 0.15643310546875 -0.6845092773437448 +1.40875 0.496002197265625 0.15643310546875 -0.6845092773437448 +1.408875 0.49530029296875 0.156219482421875 -0.6845092773437448 +1.409 0.49530029296875 0.156219482421875 -0.6845092773437448 +1.409125 0.49456787109375 0.155975341796875 -0.6845092773437448 +1.4092500000000002 0.4937744140625 0.155731201171875 -0.6845092773437448 +1.409375 0.4937744140625 0.155731201171875 -0.6845092773437448 +1.4095 0.492950439453125 0.15545654296875 -0.6845092773437448 +1.4096250000000002 0.492950439453125 0.15545654296875 -0.6845092773437448 +1.40975 0.492034912109375 0.155181884765625 -0.6845092773437448 +1.409875 0.4910888671875 0.154876708984375 -0.6845092773437448 +1.41 0.4910888671875 0.154876708984375 -0.6845092773437448 +1.410125 0.490081787109375 0.154571533203125 -0.6845092773437448 +1.41025 0.490081787109375 0.154571533203125 -0.6845092773437448 +1.410375 0.489013671875 0.15423583984375 -0.6845092773437448 +1.4105 0.487884521484375 0.15386962890625 -0.6845092773437448 +1.410625 0.487884521484375 0.15386962890625 -0.6845092773437448 +1.41075 0.486724853515625 0.15350341796875 -0.6845092773437448 +1.410875 0.486724853515625 0.15350341796875 -0.6845092773437448 +1.411 0.485504150390625 0.15313720703125 -0.6845092773437448 +1.411125 0.484222412109375 0.1527099609375 -0.6845092773437448 +1.4112500000000002 0.484222412109375 0.1527099609375 -0.6845092773437448 +1.411375 0.48291015625 0.152313232421875 -0.6845092773437448 +1.4115 0.48291015625 0.152313232421875 -0.6845092773437448 +1.4116250000000002 0.48150634765625 0.15185546875 -0.6845092773437448 +1.41175 0.480072021484375 0.15142822265625 -0.6845092773437448 +1.411875 0.480072021484375 0.15142822265625 -0.6845092773437448 +1.412 0.478607177734375 0.15093994140625 -0.6845092773437448 +1.412125 0.478607177734375 0.15093994140625 -0.6845092773437448 +1.41225 0.47705078125 0.15045166015625 -0.6845092773437448 +1.412375 0.4754638671875 0.14996337890625 -0.6845092773437448 +1.4125 0.4754638671875 0.14996337890625 -0.6845092773437448 +1.412625 0.47381591796875 0.149444580078125 -0.6845092773437448 +1.41275 0.47381591796875 0.149444580078125 -0.6845092773437448 +1.412875 0.472137451171875 0.148895263671875 -0.6845092773437448 +1.413 0.470367431640625 0.148345947265625 -0.6845092773437448 +1.413125 0.470367431640625 0.148345947265625 -0.6845092773437448 +1.4132500000000002 0.46856689453125 0.147796630859375 -0.6845092773437448 +1.413375 0.46856689453125 0.147796630859375 -0.6845092773437448 +1.4135 0.46673583984375 0.147216796875 -0.6845092773437448 +1.4136250000000002 0.464813232421875 0.1466064453125 -0.6845092773437448 +1.41375 0.464813232421875 0.1466064453125 -0.6845092773437448 +1.413875 0.462890625 0.14599609375 -0.6845092773437448 +1.414 0.462890625 0.14599609375 -0.6845092773437448 +1.414125 0.46087646484375 0.145355224609375 -0.6845092773437448 +1.41425 0.458831787109375 0.14471435546875 -0.6845092773437448 +1.414375 0.458831787109375 0.14471435546875 -0.6845092773437448 +1.4145 0.45672607421875 0.14404296875 -0.6845092773437448 +1.414625 0.45672607421875 0.14404296875 -0.6845092773437448 +1.41475 0.454559326171875 0.14337158203125 -0.6845092773437448 +1.414875 0.452362060546875 0.142669677734375 -0.6845092773437448 +1.415 0.452362060546875 0.142669677734375 -0.6845092773437448 +1.415125 0.450103759765625 0.1419677734375 -0.6845092773437448 +1.4152500000000002 0.450103759765625 0.1419677734375 -0.6845092773437448 +1.415375 0.447784423828125 0.1412353515625 -0.6845092773437448 +1.4155 0.4454345703125 0.1405029296875 -0.6845092773437448 +1.4156250000000002 0.4454345703125 0.1405029296875 -0.6845092773437448 +1.41575 0.44305419921875 0.139739990234375 -0.6845092773437448 +1.415875 0.44305419921875 0.139739990234375 -0.6845092773437448 +1.416 0.440582275390625 0.138946533203125 -0.6845092773437448 +1.416125 0.438079833984375 0.13818359375 -0.6845092773437448 +1.41625 0.438079833984375 0.13818359375 -0.6845092773437448 +1.416375 0.435546875 0.137359619140625 -0.6845092773437448 +1.4165 0.435546875 0.137359619140625 -0.6845092773437448 +1.416625 0.432952880859375 0.136566162109375 -0.6845092773437448 +1.41675 0.4302978515625 0.135711669921875 -0.6845092773437448 +1.416875 0.4302978515625 0.135711669921875 -0.6845092773437448 +1.417 0.4276123046875 0.134857177734375 -0.6845092773437448 +1.417125 0.4276123046875 0.134857177734375 -0.6845092773437448 +1.4172500000000002 0.424896240234375 0.134002685546875 -0.6845092773437448 +1.417375 0.422119140625 0.13311767578125 -0.6845092773437448 +1.4175 0.422119140625 0.13311767578125 -0.6845092773437448 +1.4176250000000002 0.419281005859375 0.132232666015625 -0.6845092773437448 +1.41775 0.419281005859375 0.132232666015625 -0.6845092773437448 +1.417875 0.416412353515625 0.131317138671875 -0.6845092773437448 +1.418 0.413482666015625 0.130401611328125 -0.6845092773437448 +1.418125 0.413482666015625 0.130401611328125 -0.6845092773437448 +1.41825 0.4105224609375 0.129486083984375 -0.6845092773437448 +1.418375 0.4105224609375 0.129486083984375 -0.6845092773437448 +1.4185 0.407501220703125 0.128509521484375 -0.6845092773437448 +1.418625 0.404449462890625 0.1275634765625 -0.6845092773437448 +1.41875 0.404449462890625 0.1275634765625 -0.6845092773437448 +1.418875 0.401336669921875 0.1265869140625 -0.6845092773437448 +1.419 0.401336669921875 0.1265869140625 -0.6845092773437448 +1.419125 0.398193359375 0.125579833984375 -0.6845092773437448 +1.4192500000000002 0.39501953125 0.12457275390625 -0.6845092773437448 +1.419375 0.39501953125 0.12457275390625 -0.6845092773437448 +1.4195 0.39178466796875 0.123565673828125 -0.6845092773437448 +1.4196250000000002 0.39178466796875 0.123565673828125 -0.6845092773437448 +1.41975 0.388519287109375 0.122528076171875 -0.6845092773437448 +1.419875 0.38519287109375 0.121490478515625 -0.6845092773437448 +1.42 0.38519287109375 0.121490478515625 -0.6845092773437448 +1.420125 0.3818359375 0.12042236328125 -0.6845092773437448 +1.42025 0.3818359375 0.12042236328125 -0.6845092773437448 +1.420375 0.378448486328125 0.119354248046875 -0.6845092773437448 +1.4205 0.375 0.118255615234375 -0.6845092773437448 +1.420625 0.375 0.118255615234375 -0.6845092773437448 +1.42075 0.37152099609375 0.117156982421875 -0.6845092773437448 +1.420875 0.37152099609375 0.117156982421875 -0.6845092773437448 +1.421 0.36798095703125 0.116058349609375 -0.6845092773437448 +1.421125 0.36444091796875 0.11492919921875 -0.6845092773437448 +1.4212500000000002 0.36444091796875 0.11492919921875 -0.6845092773437448 +1.421375 0.360809326171875 0.113800048828125 -0.6845092773437448 +1.4215 0.360809326171875 0.113800048828125 -0.6845092773437448 +1.4216250000000002 0.357177734375 0.112640380859375 -0.6845092773437448 +1.42175 0.353485107421875 0.111480712890625 -0.6845092773437448 +1.421875 0.353485107421875 0.111480712890625 -0.6845092773437448 +1.422 0.349761962890625 0.110321044921875 -0.6845092773437448 +1.422125 0.349761962890625 0.110321044921875 -0.6845092773437448 +1.42225 0.34600830078125 0.109130859375 -0.6845092773437448 +1.422375 0.34222412109375 0.107940673828125 -0.6845092773437448 +1.4225 0.34222412109375 0.107940673828125 -0.6845092773437448 +1.422625 0.33837890625 0.106719970703125 -0.6845092773437448 +1.42275 0.33837890625 0.106719970703125 -0.6845092773437448 +1.4228750000000002 0.334503173828125 0.105499267578125 -0.6845092773437448 +1.423 0.330596923828125 0.104278564453125 -0.6845092773437448 +1.423125 0.330596923828125 0.104278564453125 -0.6845092773437448 +1.4232500000000002 0.32666015625 0.10302734375 -0.6845092773437448 +1.423375 0.32666015625 0.10302734375 -0.6845092773437448 +1.4235 0.322662353515625 0.101776123046875 -0.6845092773437448 +1.4236250000000002 0.31866455078125 0.100494384765625 -0.6845092773437448 +1.42375 0.31866455078125 0.100494384765625 -0.6845092773437448 +1.423875 0.314605712890625 0.099212646484375 -0.6845092773437448 +1.424 0.314605712890625 0.099212646484375 -0.6845092773437448 +1.424125 0.310516357421875 0.097930908203125 -0.6845092773437448 +1.42425 0.306396484375 0.096649169921875 -0.6845092773437448 +1.424375 0.306396484375 0.096649169921875 -0.6845092773437448 +1.4245 0.30224609375 0.0953369140625 -0.6845092773437448 +1.424625 0.30224609375 0.0953369140625 -0.6845092773437448 +1.42475 0.298065185546875 0.093994140625 -0.6845092773437448 +1.4248750000000002 0.2938232421875 0.092681884765625 -0.6845092773437448 +1.425 0.2938232421875 0.092681884765625 -0.6845092773437448 +1.425125 0.289581298828125 0.091339111328125 -0.6845092773437448 +1.4252500000000002 0.289581298828125 0.091339111328125 -0.6845092773437448 +1.425375 0.285308837890625 0.0899658203125 -0.6845092773437448 +1.4255 0.280975341796875 0.088623046875 -0.6845092773437448 +1.425625 0.280975341796875 0.088623046875 -0.6845092773437448 +1.42575 0.276641845703125 0.087249755859375 -0.6845092773437448 +1.425875 0.276641845703125 0.087249755859375 -0.6845092773437448 +1.426 0.27227783203125 0.08587646484375 -0.6845092773437448 +1.426125 0.267852783203125 0.08447265625 -0.6845092773437448 +1.42625 0.267852783203125 0.08447265625 -0.6845092773437448 +1.426375 0.263427734375 0.08306884765625 -0.6845092773437448 +1.4265 0.263427734375 0.08306884765625 -0.6845092773437448 +1.426625 0.25897216796875 0.0816650390625 -0.6845092773437448 +1.42675 0.25445556640625 0.08026123046875 -0.6845092773437448 +1.4268750000000002 0.25445556640625 0.08026123046875 -0.6845092773437448 +1.427 0.249969482421875 0.078826904296875 -0.6845092773437448 +1.427125 0.249969482421875 0.078826904296875 -0.6845092773437448 +1.4272500000000002 0.24542236328125 0.077392578125 -0.6845092773437448 +1.427375 0.2408447265625 0.075958251953125 -0.6845092773437448 +1.4275 0.2408447265625 0.075958251953125 -0.6845092773437448 +1.427625 0.236236572265625 0.074493408203125 -0.6845092773437448 +1.42775 0.236236572265625 0.074493408203125 -0.6845092773437448 +1.427875 0.231597900390625 0.073028564453125 -0.6845092773437448 +1.428 0.226959228515625 0.071563720703125 -0.6845092773437448 +1.428125 0.226959228515625 0.071563720703125 -0.6845092773437448 +1.42825 0.2222900390625 0.070098876953125 -0.6845092773437448 +1.428375 0.2222900390625 0.070098876953125 -0.6845092773437448 +1.4285 0.21759033203125 0.068603515625 -0.6845092773437448 +1.428625 0.212860107421875 0.067138671875 -0.6845092773437448 +1.42875 0.212860107421875 0.067138671875 -0.6845092773437448 +1.4288750000000002 0.208099365234375 0.065643310546875 -0.6845092773437448 +1.429 0.208099365234375 0.065643310546875 -0.6845092773437448 +1.429125 0.203338623046875 0.064117431640625 -0.6845092773437448 +1.4292500000000002 0.19854736328125 0.0626220703125 -0.6845092773437448 +1.429375 0.19854736328125 0.0626220703125 -0.6845092773437448 +1.4295 0.1937255859375 0.06109619140625 -0.6845092773437448 +1.429625 0.1937255859375 0.06109619140625 -0.6845092773437448 +1.42975 0.188873291015625 0.0595703125 -0.6845092773437448 +1.429875 0.18402099609375 0.05804443359375 -0.6845092773437448 +1.43 0.18402099609375 0.05804443359375 -0.6845092773437448 +1.430125 0.17913818359375 0.056488037109375 -0.6845092773437448 +1.43025 0.17913818359375 0.056488037109375 -0.6845092773437448 +1.430375 0.17425537109375 0.054962158203125 -0.6845092773437448 +1.4305 0.169342041015625 0.05340576171875 -0.6845092773437448 +1.430625 0.169342041015625 0.05340576171875 -0.6845092773437448 +1.43075 0.164398193359375 0.051849365234375 -0.6845092773437448 +1.4308750000000002 0.164398193359375 0.051849365234375 -0.6845092773437448 +1.431 0.159454345703125 0.05029296875 -0.6845092773437448 +1.431125 0.15447998046875 0.0487060546875 -0.6845092773437448 +1.4312500000000002 0.15447998046875 0.0487060546875 -0.6845092773437448 +1.431375 0.14947509765625 0.047149658203125 -0.6845092773437448 +1.4315 0.14947509765625 0.047149658203125 -0.6845092773437448 +1.431625 0.14447021484375 0.045562744140625 -0.6845092773437448 +1.43175 0.13946533203125 0.043975830078125 -0.6845092773437448 +1.431875 0.13946533203125 0.043975830078125 -0.6845092773437448 +1.432 0.134429931640625 0.042388916015625 -0.6845092773437448 +1.432125 0.134429931640625 0.042388916015625 -0.6845092773437448 +1.43225 0.129364013671875 0.040802001953125 -0.6845092773437448 +1.432375 0.124298095703125 0.0391845703125 -0.6845092773437448 +1.4325 0.124298095703125 0.0391845703125 -0.6845092773437448 +1.432625 0.119232177734375 0.03759765625 -0.6845092773437448 +1.43275 0.119232177734375 0.03759765625 -0.6845092773437448 +1.4328750000000002 0.1141357421875 0.035980224609375 -0.6845092773437448 +1.433 0.109039306640625 0.034393310546875 -0.6845092773437448 +1.433125 0.109039306640625 0.034393310546875 -0.6845092773437448 +1.4332500000000002 0.103912353515625 0.03277587890625 -0.6845092773437448 +1.433375 0.103912353515625 0.03277587890625 -0.6845092773437448 +1.4335 0.098785400390625 0.031158447265625 -0.6845092773437448 +1.433625 0.093658447265625 0.029541015625 -0.6845092773437448 +1.43375 0.093658447265625 0.029541015625 -0.6845092773437448 +1.433875 0.0885009765625 0.02789306640625 -0.6845092773437448 +1.434 0.0885009765625 0.02789306640625 -0.6845092773437448 +1.434125 0.083343505859375 0.026275634765625 -0.6845092773437448 +1.43425 0.07818603515625 0.024658203125 -0.6845092773437448 +1.434375 0.07818603515625 0.024658203125 -0.6845092773437448 +1.4345 0.072998046875 0.02301025390625 -0.6845092773437448 +1.434625 0.072998046875 0.02301025390625 -0.6845092773437448 +1.43475 0.06781005859375 0.021392822265625 -0.6845092773437448 +1.4348750000000002 0.0626220703125 0.019744873046875 -0.6845092773437448 +1.435 0.0626220703125 0.019744873046875 -0.6845092773437448 +1.435125 0.05743408203125 0.018096923828125 -0.6845092773437448 +1.4352500000000002 0.05743408203125 0.018096923828125 -0.6845092773437448 +1.435375 0.05224609375 0.0164794921875 -0.6845092773437448 +1.4355 0.047027587890625 0.01483154296875 -0.6845092773437448 +1.435625 0.047027587890625 0.01483154296875 -0.6845092773437448 +1.43575 0.04180908203125 0.01318359375 -0.6845092773437448 +1.435875 0.04180908203125 0.01318359375 -0.6845092773437448 +1.436 0.036590576171875 0.01153564453125 -0.6845092773437448 +1.436125 0.0313720703125 0.0098876953125 -0.6845092773437448 +1.43625 0.0313720703125 0.0098876953125 -0.6845092773437448 +1.436375 0.026123046875 0.00823974609375 -0.6845092773437448 +1.4365 0.026123046875 0.00823974609375 -0.6845092773437448 +1.436625 0.020904541015625 0.006591796875 -0.6845092773437448 +1.43675 0.01568603515625 0.00494384765625 -0.6845092773437448 +1.4368750000000002 0.01568603515625 0.00494384765625 -0.6845092773437448 +1.437 0.01043701171875 0.0032958984375 -0.6845092773437448 +1.437125 0.01043701171875 0.0032958984375 -0.6845092773437448 +1.4372500000000002 0.005218505859375 0.00164794921875 -0.6845092773437448 +1.437375 0.0 0.0 -0.6845092773437448 +1.4375 0.0 0.0 -0.6845092773437448 +1.437625 -0.005218505859375 -0.00164794921875 -0.6845092773437448 +1.43775 -0.005218505859375 -0.00164794921875 -0.6845092773437448 +1.437875 -0.01043701171875 -0.0032958984375 -0.6845092773437448 +1.438 -0.01568603515625 -0.00494384765625 -0.6845092773437448 +1.438125 -0.01568603515625 -0.00494384765625 -0.6845092773437448 +1.43825 -0.020904541015625 -0.006591796875 -0.6845092773437448 +1.438375 -0.020904541015625 -0.006591796875 -0.6845092773437448 +1.4385000000000002 -0.026123046875 -0.00823974609375 -0.6845092773437448 +1.438625 -0.0313720703125 -0.0098876953125 -0.6845092773437448 +1.43875 -0.0313720703125 -0.0098876953125 -0.6845092773437448 +1.4388750000000002 -0.036590576171875 -0.01153564453125 -0.6845092773437448 +1.439 -0.036590576171875 -0.01153564453125 -0.6845092773437448 +1.439125 -0.04180908203125 -0.01318359375 -0.6845092773437448 +1.4392500000000002 -0.047027587890625 -0.01483154296875 -0.6845092773437448 +1.439375 -0.047027587890625 -0.01483154296875 -0.6845092773437448 +1.4395 -0.05224609375 -0.0164794921875 -0.6845092773437448 +1.439625 -0.05224609375 -0.0164794921875 -0.6845092773437448 +1.43975 -0.05743408203125 -0.018096923828125 -0.6845092773437448 +1.439875 -0.0626220703125 -0.019744873046875 -0.6845092773437448 +1.44 -0.0626220703125 -0.041046142578125 -0.3446105957031174 +1.440125 -0.06781005859375 -0.04443359375 -0.3446105957031174 +1.44025 -0.06781005859375 -0.04443359375 -0.3446105957031174 +1.440375 -0.072998046875 -0.0478515625 -0.3446105957031174 +1.4405000000000002 -0.07818603515625 -0.051239013671875 -0.3446105957031174 +1.440625 -0.07818603515625 -0.051239013671875 -0.3446105957031174 +1.44075 -0.083343505859375 -0.05462646484375 -0.3446105957031174 +1.4408750000000002 -0.083343505859375 -0.05462646484375 -0.3446105957031174 +1.441 -0.0885009765625 -0.058013916015625 -0.3446105957031174 +1.441125 -0.093658447265625 -0.061370849609375 -0.3446105957031174 +1.44125 -0.093658447265625 -0.061370849609375 -0.3446105957031174 +1.441375 -0.098785400390625 -0.064727783203125 -0.3446105957031174 +1.4415 -0.098785400390625 -0.064727783203125 -0.3446105957031174 +1.441625 -0.103912353515625 -0.068084716796875 -0.3446105957031174 +1.44175 -0.109039306640625 -0.071441650390625 -0.3446105957031174 +1.441875 -0.109039306640625 -0.071441650390625 -0.3446105957031174 +1.442 -0.1141357421875 -0.074798583984375 -0.3446105957031174 +1.442125 -0.1141357421875 -0.074798583984375 -0.3446105957031174 +1.44225 -0.119232177734375 -0.078125 -0.3446105957031174 +1.442375 -0.124298095703125 -0.081451416015625 -0.3446105957031174 +1.4425000000000002 -0.124298095703125 -0.081451416015625 -0.3446105957031174 +1.442625 -0.129364013671875 -0.08477783203125 -0.3446105957031174 +1.44275 -0.129364013671875 -0.08477783203125 -0.3446105957031174 +1.4428750000000002 -0.134429931640625 -0.08807373046875 -0.3446105957031174 +1.443 -0.13946533203125 -0.091400146484375 -0.3446105957031174 +1.443125 -0.13946533203125 -0.091400146484375 -0.3446105957031174 +1.44325 -0.14447021484375 -0.09466552734375 -0.3446105957031174 +1.443375 -0.14447021484375 -0.09466552734375 -0.3446105957031174 +1.4435 -0.14947509765625 -0.09796142578125 -0.3446105957031174 +1.443625 -0.15447998046875 -0.101226806640625 -0.3446105957031174 +1.44375 -0.15447998046875 -0.101226806640625 -0.3446105957031174 +1.443875 -0.159454345703125 -0.1044921875 -0.3446105957031174 +1.444 -0.159454345703125 -0.1044921875 -0.3446105957031174 +1.444125 -0.164398193359375 -0.10772705078125 -0.3446105957031174 +1.44425 -0.169342041015625 -0.1109619140625 -0.3446105957031174 +1.444375 -0.169342041015625 -0.1109619140625 -0.3446105957031174 +1.4445000000000002 -0.17425537109375 -0.11419677734375 -0.3446105957031174 +1.444625 -0.17425537109375 -0.11419677734375 -0.3446105957031174 +1.44475 -0.17913818359375 -0.117401123046875 -0.3446105957031174 +1.4448750000000002 -0.18402099609375 -0.12060546875 -0.3446105957031174 +1.445 -0.18402099609375 -0.12060546875 -0.3446105957031174 +1.445125 -0.188873291015625 -0.123779296875 -0.3446105957031174 +1.44525 -0.188873291015625 -0.123779296875 -0.3446105957031174 +1.445375 -0.1937255859375 -0.126953125 -0.3446105957031174 +1.4455 -0.19854736328125 -0.130096435546875 -0.3446105957031174 +1.445625 -0.19854736328125 -0.130096435546875 -0.3446105957031174 +1.44575 -0.203338623046875 -0.13323974609375 -0.3446105957031174 +1.445875 -0.203338623046875 -0.13323974609375 -0.3446105957031174 +1.446 -0.208099365234375 -0.136383056640625 -0.3446105957031174 +1.446125 -0.212860107421875 -0.139495849609375 -0.3446105957031174 +1.44625 -0.212860107421875 -0.139495849609375 -0.3446105957031174 +1.446375 -0.21759033203125 -0.142578125 -0.3446105957031174 +1.4465000000000002 -0.21759033203125 -0.142578125 -0.3446105957031174 +1.446625 -0.2222900390625 -0.145660400390625 -0.3446105957031174 +1.44675 -0.226959228515625 -0.148712158203125 -0.3446105957031174 +1.4468750000000002 -0.226959228515625 -0.148712158203125 -0.3446105957031174 +1.447 -0.231597900390625 -0.151763916015625 -0.3446105957031174 +1.447125 -0.231597900390625 -0.151763916015625 -0.3446105957031174 +1.44725 -0.236236572265625 -0.154815673828125 -0.3446105957031174 +1.447375 -0.2408447265625 -0.157806396484375 -0.3446105957031174 +1.4475 -0.2408447265625 -0.157806396484375 -0.3446105957031174 +1.447625 -0.24542236328125 -0.16082763671875 -0.3446105957031174 +1.44775 -0.24542236328125 -0.16082763671875 -0.3446105957031174 +1.447875 -0.249969482421875 -0.163787841796875 -0.3446105957031174 +1.448 -0.25445556640625 -0.166748046875 -0.3446105957031174 +1.448125 -0.25445556640625 -0.166748046875 -0.3446105957031174 +1.44825 -0.25897216796875 -0.169708251953125 -0.3446105957031174 +1.448375 -0.25897216796875 -0.169708251953125 -0.3446105957031174 +1.4485000000000002 -0.263427734375 -0.172637939453125 -0.3446105957031174 +1.448625 -0.267852783203125 -0.175537109375 -0.3446105957031174 +1.44875 -0.267852783203125 -0.175537109375 -0.3446105957031174 +1.4488750000000002 -0.27227783203125 -0.178436279296875 -0.3446105957031174 +1.449 -0.27227783203125 -0.178436279296875 -0.3446105957031174 +1.449125 -0.276641845703125 -0.1812744140625 -0.3446105957031174 +1.44925 -0.280975341796875 -0.18414306640625 -0.3446105957031174 +1.449375 -0.280975341796875 -0.18414306640625 -0.3446105957031174 +1.4495 -0.285308837890625 -0.186981201171875 -0.3446105957031174 +1.449625 -0.285308837890625 -0.186981201171875 -0.3446105957031174 +1.44975 -0.289581298828125 -0.189788818359375 -0.3446105957031174 +1.449875 -0.2938232421875 -0.19256591796875 -0.3446105957031174 +1.45 -0.2938232421875 -0.19256591796875 -0.3446105957031174 +1.450125 -0.298065185546875 -0.195343017578125 -0.3446105957031174 +1.45025 -0.298065185546875 -0.195343017578125 -0.3446105957031174 +1.450375 -0.30224609375 -0.19805908203125 -0.3446105957031174 +1.4505000000000002 -0.306396484375 -0.2008056640625 -0.3446105957031174 +1.450625 -0.306396484375 -0.2008056640625 -0.3446105957031174 +1.45075 -0.310516357421875 -0.2034912109375 -0.3446105957031174 +1.4508750000000002 -0.310516357421875 -0.2034912109375 -0.3446105957031174 +1.451 -0.314605712890625 -0.2061767578125 -0.3446105957031174 +1.451125 -0.31866455078125 -0.208831787109375 -0.3446105957031174 +1.45125 -0.31866455078125 -0.208831787109375 -0.3446105957031174 +1.451375 -0.322662353515625 -0.211456298828125 -0.3446105957031174 +1.4515 -0.322662353515625 -0.211456298828125 -0.3446105957031174 +1.451625 -0.32666015625 -0.214080810546875 -0.3446105957031174 +1.45175 -0.330596923828125 -0.216644287109375 -0.3446105957031174 +1.451875 -0.330596923828125 -0.216644287109375 -0.3446105957031174 +1.452 -0.334503173828125 -0.219207763671875 -0.3446105957031174 +1.452125 -0.334503173828125 -0.219207763671875 -0.3446105957031174 +1.45225 -0.33837890625 -0.22174072265625 -0.3446105957031174 +1.452375 -0.34222412109375 -0.224273681640625 -0.3446105957031174 +1.4525000000000002 -0.34222412109375 -0.224273681640625 -0.3446105957031174 +1.452625 -0.34600830078125 -0.22674560546875 -0.3446105957031174 +1.45275 -0.34600830078125 -0.22674560546875 -0.3446105957031174 +1.4528750000000002 -0.349761962890625 -0.229217529296875 -0.3446105957031174 +1.453 -0.353485107421875 -0.231658935546875 -0.3446105957031174 +1.453125 -0.353485107421875 -0.231658935546875 -0.3446105957031174 +1.45325 -0.357177734375 -0.23406982421875 -0.3446105957031174 +1.453375 -0.357177734375 -0.23406982421875 -0.3446105957031174 +1.4535 -0.360809326171875 -0.2364501953125 -0.3446105957031174 +1.453625 -0.36444091796875 -0.23883056640625 -0.3446105957031174 +1.45375 -0.36444091796875 -0.23883056640625 -0.3446105957031174 +1.453875 -0.36798095703125 -0.24114990234375 -0.3446105957031174 +1.454 -0.36798095703125 -0.24114990234375 -0.3446105957031174 +1.4541250000000002 -0.37152099609375 -0.24346923828125 -0.3446105957031174 +1.45425 -0.375 -0.245758056640625 -0.3446105957031174 +1.454375 -0.375 -0.245758056640625 -0.3446105957031174 +1.4545000000000002 -0.378448486328125 -0.248016357421875 -0.3446105957031174 +1.454625 -0.378448486328125 -0.248016357421875 -0.3446105957031174 +1.45475 -0.3818359375 -0.250244140625 -0.3446105957031174 +1.4548750000000002 -0.38519287109375 -0.25244140625 -0.3446105957031174 +1.455 -0.38519287109375 -0.25244140625 -0.3446105957031174 +1.455125 -0.388519287109375 -0.254608154296875 -0.3446105957031174 +1.45525 -0.388519287109375 -0.254608154296875 -0.3446105957031174 +1.455375 -0.39178466796875 -0.256744384765625 -0.3446105957031174 +1.4555 -0.39501953125 -0.258880615234375 -0.3446105957031174 +1.455625 -0.39501953125 -0.258880615234375 -0.3446105957031174 +1.45575 -0.398193359375 -0.260955810546875 -0.3446105957031174 +1.455875 -0.398193359375 -0.260955810546875 -0.3446105957031174 +1.456 -0.401336669921875 -0.26300048828125 -0.3446105957031174 +1.4561250000000002 -0.404449462890625 -0.265045166015625 -0.3446105957031174 +1.45625 -0.404449462890625 -0.265045166015625 -0.3446105957031174 +1.456375 -0.407501220703125 -0.267059326171875 -0.3446105957031174 +1.4565000000000002 -0.407501220703125 -0.267059326171875 -0.3446105957031174 +1.456625 -0.4105224609375 -0.269012451171875 -0.3446105957031174 +1.45675 -0.413482666015625 -0.270965576171875 -0.3446105957031174 +1.456875 -0.413482666015625 -0.270965576171875 -0.3446105957031174 +1.457 -0.416412353515625 -0.27288818359375 -0.3446105957031174 +1.457125 -0.416412353515625 -0.27288818359375 -0.3446105957031174 +1.45725 -0.419281005859375 -0.274749755859375 -0.3446105957031174 +1.457375 -0.422119140625 -0.276611328125 -0.3446105957031174 +1.4575 -0.422119140625 -0.276611328125 -0.3446105957031174 +1.457625 -0.424896240234375 -0.2784423828125 -0.3446105957031174 +1.45775 -0.424896240234375 -0.2784423828125 -0.3446105957031174 +1.457875 -0.4276123046875 -0.280242919921875 -0.3446105957031174 +1.458 -0.4302978515625 -0.281982421875 -0.3446105957031174 +1.4581250000000002 -0.4302978515625 -0.281982421875 -0.3446105957031174 +1.45825 -0.432952880859375 -0.283721923828125 -0.3446105957031174 +1.458375 -0.432952880859375 -0.283721923828125 -0.3446105957031174 +1.4585000000000002 -0.435546875 -0.285430908203125 -0.3446105957031174 +1.458625 -0.438079833984375 -0.287078857421875 -0.3446105957031174 +1.45875 -0.438079833984375 -0.287078857421875 -0.3446105957031174 +1.458875 -0.440582275390625 -0.288726806640625 -0.3446105957031174 +1.459 -0.440582275390625 -0.288726806640625 -0.3446105957031174 +1.459125 -0.44305419921875 -0.29034423828125 -0.3446105957031174 +1.45925 -0.4454345703125 -0.291900634765625 -0.3446105957031174 +1.459375 -0.4454345703125 -0.291900634765625 -0.3446105957031174 +1.4595 -0.447784423828125 -0.29345703125 -0.3446105957031174 +1.459625 -0.447784423828125 -0.29345703125 -0.3446105957031174 +1.45975 -0.450103759765625 -0.294952392578125 -0.3446105957031174 +1.459875 -0.452362060546875 -0.29644775390625 -0.3446105957031174 +1.46 -0.452362060546875 -0.29644775390625 -0.3446105957031174 +1.4601250000000002 -0.454559326171875 -0.297882080078125 -0.3446105957031174 +1.46025 -0.454559326171875 -0.297882080078125 -0.3446105957031174 +1.460375 -0.45672607421875 -0.299285888671875 -0.3446105957031174 +1.4605000000000002 -0.458831787109375 -0.300689697265625 -0.3446105957031174 +1.460625 -0.458831787109375 -0.300689697265625 -0.3446105957031174 +1.46075 -0.46087646484375 -0.302032470703125 -0.3446105957031174 +1.460875 -0.46087646484375 -0.302032470703125 -0.3446105957031174 +1.461 -0.462890625 -0.3033447265625 -0.3446105957031174 +1.461125 -0.464813232421875 -0.304595947265625 -0.3446105957031174 +1.46125 -0.464813232421875 -0.304595947265625 -0.3446105957031174 +1.461375 -0.46673583984375 -0.30584716796875 -0.3446105957031174 +1.4615 -0.46673583984375 -0.30584716796875 -0.3446105957031174 +1.461625 -0.46856689453125 -0.30706787109375 -0.3446105957031174 +1.46175 -0.470367431640625 -0.308258056640625 -0.3446105957031174 +1.461875 -0.470367431640625 -0.308258056640625 -0.3446105957031174 +1.462 -0.472137451171875 -0.30938720703125 -0.3446105957031174 +1.4621250000000002 -0.472137451171875 -0.30938720703125 -0.3446105957031174 +1.46225 -0.47381591796875 -0.310516357421875 -0.3446105957031174 +1.462375 -0.4754638671875 -0.31158447265625 -0.3446105957031174 +1.4625000000000002 -0.4754638671875 -0.31158447265625 -0.3446105957031174 +1.462625 -0.47705078125 -0.3126220703125 -0.3446105957031174 +1.46275 -0.47705078125 -0.3126220703125 -0.3446105957031174 +1.462875 -0.478607177734375 -0.313629150390625 -0.3446105957031174 +1.463 -0.480072021484375 -0.314605712890625 -0.3446105957031174 +1.463125 -0.480072021484375 -0.314605712890625 -0.3446105957031174 +1.46325 -0.48150634765625 -0.3155517578125 -0.3446105957031174 +1.463375 -0.48150634765625 -0.3155517578125 -0.3446105957031174 +1.4635 -0.48291015625 -0.31646728515625 -0.3446105957031174 +1.463625 -0.484222412109375 -0.31732177734375 -0.3446105957031174 +1.46375 -0.484222412109375 -0.31732177734375 -0.3446105957031174 +1.463875 -0.485504150390625 -0.31817626953125 -0.3446105957031174 +1.464 -0.485504150390625 -0.31817626953125 -0.3446105957031174 +1.4641250000000002 -0.486724853515625 -0.3189697265625 -0.3446105957031174 +1.46425 -0.487884521484375 -0.319732666015625 -0.3446105957031174 +1.464375 -0.487884521484375 -0.319732666015625 -0.3446105957031174 +1.4645000000000002 -0.489013671875 -0.320465087890625 -0.3446105957031174 +1.464625 -0.489013671875 -0.320465087890625 -0.3446105957031174 +1.46475 -0.490081787109375 -0.3211669921875 -0.3446105957031174 +1.464875 -0.4910888671875 -0.321807861328125 -0.3446105957031174 +1.465 -0.4910888671875 -0.321807861328125 -0.3446105957031174 +1.465125 -0.492034912109375 -0.32244873046875 -0.3446105957031174 +1.46525 -0.492034912109375 -0.32244873046875 -0.3446105957031174 +1.465375 -0.492950439453125 -0.323028564453125 -0.3446105957031174 +1.4655 -0.4937744140625 -0.323577880859375 -0.3446105957031174 +1.465625 -0.4937744140625 -0.323577880859375 -0.3446105957031174 +1.46575 -0.49456787109375 -0.3240966796875 -0.3446105957031174 +1.465875 -0.49456787109375 -0.3240966796875 -0.3446105957031174 +1.466 -0.49530029296875 -0.3245849609375 -0.3446105957031174 +1.4661250000000002 -0.496002197265625 -0.325042724609375 -0.3446105957031174 +1.46625 -0.496002197265625 -0.325042724609375 -0.3446105957031174 +1.466375 -0.496612548828125 -0.325439453125 -0.3446105957031174 +1.4665000000000002 -0.496612548828125 -0.325439453125 -0.3446105957031174 +1.466625 -0.4971923828125 -0.325836181640625 -0.3446105957031174 +1.46675 -0.497711181640625 -0.326171875 -0.3446105957031174 +1.466875 -0.497711181640625 -0.326171875 -0.3446105957031174 +1.467 -0.498199462890625 -0.32647705078125 -0.3446105957031174 +1.467125 -0.498199462890625 -0.32647705078125 -0.3446105957031174 +1.46725 -0.49859619140625 -0.326751708984375 -0.3446105957031174 +1.467375 -0.49896240234375 -0.32696533203125 -0.3446105957031174 +1.4675 -0.49896240234375 -0.32696533203125 -0.3446105957031174 +1.467625 -0.499267578125 -0.327178955078125 -0.3446105957031174 +1.46775 -0.499267578125 -0.327178955078125 -0.3446105957031174 +1.467875 -0.49951171875 -0.32733154296875 -0.3446105957031174 +1.468 -0.49969482421875 -0.32745361328125 -0.3446105957031174 +1.4681250000000002 -0.49969482421875 -0.32745361328125 -0.3446105957031174 +1.46825 -0.49981689453125 -0.327545166015625 -0.3446105957031174 +1.468375 -0.49981689453125 -0.327545166015625 -0.3446105957031174 +1.4685000000000002 -0.499908447265625 -0.327606201171875 -0.3446105957031174 +1.468625 -0.49993896484375 -0.32763671875 -0.3446105957031174 +1.46875 -0.49993896484375 -0.32763671875 -0.3446105957031174 +1.468875 -0.499908447265625 -0.327606201171875 -0.3446105957031174 +1.469 -0.499908447265625 -0.327606201171875 -0.3446105957031174 +1.469125 -0.49981689453125 -0.327545166015625 -0.3446105957031174 +1.46925 -0.49969482421875 -0.32745361328125 -0.3446105957031174 +1.469375 -0.49969482421875 -0.32745361328125 -0.3446105957031174 +1.4695 -0.49951171875 -0.32733154296875 -0.3446105957031174 +1.469625 -0.49951171875 -0.32733154296875 -0.3446105957031174 +1.4697500000000002 -0.499267578125 -0.327178955078125 -0.3446105957031174 +1.469875 -0.49896240234375 -0.32696533203125 -0.3446105957031174 +1.47 -0.49896240234375 -0.32696533203125 -0.3446105957031174 +1.4701250000000002 -0.49859619140625 -0.326751708984375 -0.3446105957031174 +1.47025 -0.49859619140625 -0.326751708984375 -0.3446105957031174 +1.470375 -0.498199462890625 -0.32647705078125 -0.3446105957031174 +1.4705000000000002 -0.497711181640625 -0.326171875 -0.3446105957031174 +1.470625 -0.497711181640625 -0.326171875 -0.3446105957031174 +1.47075 -0.4971923828125 -0.325836181640625 -0.3446105957031174 +1.470875 -0.4971923828125 -0.325836181640625 -0.3446105957031174 +1.471 -0.496612548828125 -0.325439453125 -0.3446105957031174 +1.471125 -0.496002197265625 -0.325042724609375 -0.3446105957031174 +1.47125 -0.496002197265625 -0.325042724609375 -0.3446105957031174 +1.471375 -0.49530029296875 -0.3245849609375 -0.3446105957031174 +1.4715 -0.49530029296875 -0.3245849609375 -0.3446105957031174 +1.471625 -0.49456787109375 -0.3240966796875 -0.3446105957031174 +1.4717500000000002 -0.4937744140625 -0.323577880859375 -0.3446105957031174 +1.471875 -0.4937744140625 -0.323577880859375 -0.3446105957031174 +1.472 -0.468170166015625 -0.492950439453125 0.050219726562508388 +1.4721250000000002 -0.468170166015625 -0.492950439453125 0.050219726562508388 +1.47225 -0.467315673828125 -0.492034912109375 0.050219726562508388 +1.472375 -0.466400146484375 -0.4910888671875 0.050219726562508388 +1.4725 -0.466400146484375 -0.4910888671875 0.050219726562508388 +1.472625 -0.465423583984375 -0.490081787109375 0.050219726562508388 +1.47275 -0.465423583984375 -0.490081787109375 0.050219726562508388 +1.472875 -0.46441650390625 -0.489013671875 0.050219726562508388 +1.473 -0.463348388671875 -0.487884521484375 0.050219726562508388 +1.473125 -0.463348388671875 -0.487884521484375 0.050219726562508388 +1.47325 -0.462249755859375 -0.486724853515625 0.050219726562508388 +1.473375 -0.462249755859375 -0.486724853515625 0.050219726562508388 +1.4735 -0.461090087890625 -0.485504150390625 0.050219726562508388 +1.473625 -0.459869384765625 -0.484222412109375 0.050219726562508388 +1.4737500000000002 -0.459869384765625 -0.484222412109375 0.050219726562508388 +1.473875 -0.4586181640625 -0.48291015625 0.050219726562508388 +1.474 -0.4586181640625 -0.48291015625 0.050219726562508388 +1.4741250000000002 -0.457305908203125 -0.48150634765625 0.050219726562508388 +1.47425 -0.4559326171875 -0.480072021484375 0.050219726562508388 +1.474375 -0.4559326171875 -0.480072021484375 0.050219726562508388 +1.4745 -0.45452880859375 -0.478607177734375 0.050219726562508388 +1.474625 -0.45452880859375 -0.478607177734375 0.050219726562508388 +1.47475 -0.45306396484375 -0.47705078125 0.050219726562508388 +1.474875 -0.451568603515625 -0.4754638671875 0.050219726562508388 +1.475 -0.451568603515625 -0.4754638671875 0.050219726562508388 +1.475125 -0.45001220703125 -0.47381591796875 0.050219726562508388 +1.47525 -0.45001220703125 -0.47381591796875 0.050219726562508388 +1.475375 -0.448394775390625 -0.472137451171875 0.050219726562508388 +1.4755 -0.44671630859375 -0.470367431640625 0.050219726562508388 +1.475625 -0.44671630859375 -0.470367431640625 0.050219726562508388 +1.4757500000000002 -0.44500732421875 -0.46856689453125 0.050219726562508388 +1.475875 -0.44500732421875 -0.46856689453125 0.050219726562508388 +1.476 -0.443267822265625 -0.46673583984375 0.050219726562508388 +1.4761250000000002 -0.441436767578125 -0.464813232421875 0.050219726562508388 +1.47625 -0.441436767578125 -0.464813232421875 0.050219726562508388 +1.476375 -0.439605712890625 -0.462890625 0.050219726562508388 +1.4765 -0.439605712890625 -0.462890625 0.050219726562508388 +1.476625 -0.43768310546875 -0.46087646484375 0.050219726562508388 +1.47675 -0.435760498046875 -0.458831787109375 0.050219726562508388 +1.476875 -0.435760498046875 -0.458831787109375 0.050219726562508388 +1.477 -0.433746337890625 -0.45672607421875 0.050219726562508388 +1.477125 -0.433746337890625 -0.45672607421875 0.050219726562508388 +1.47725 -0.43170166015625 -0.454559326171875 0.050219726562508388 +1.477375 -0.42962646484375 -0.452362060546875 0.050219726562508388 +1.4775 -0.42962646484375 -0.452362060546875 0.050219726562508388 +1.477625 -0.427459716796875 -0.450103759765625 0.050219726562508388 +1.4777500000000002 -0.427459716796875 -0.450103759765625 0.050219726562508388 +1.477875 -0.42529296875 -0.447784423828125 0.050219726562508388 +1.478 -0.42303466796875 -0.4454345703125 0.050219726562508388 +1.4781250000000002 -0.42303466796875 -0.4454345703125 0.050219726562508388 +1.47825 -0.4207763671875 -0.44305419921875 0.050219726562508388 +1.478375 -0.4207763671875 -0.44305419921875 0.050219726562508388 +1.4785 -0.418426513671875 -0.440582275390625 0.050219726562508388 +1.478625 -0.41607666015625 -0.438079833984375 0.050219726562508388 +1.47875 -0.41607666015625 -0.438079833984375 0.050219726562508388 +1.478875 -0.413665771484375 -0.435546875 0.050219726562508388 +1.479 -0.413665771484375 -0.435546875 0.050219726562508388 +1.479125 -0.41119384765625 -0.432952880859375 0.050219726562508388 +1.47925 -0.408660888671875 -0.4302978515625 0.050219726562508388 +1.479375 -0.408660888671875 -0.4302978515625 0.050219726562508388 +1.4795 -0.4061279296875 -0.4276123046875 0.050219726562508388 +1.479625 -0.4061279296875 -0.4276123046875 0.050219726562508388 +1.4797500000000002 -0.403533935546875 -0.424896240234375 0.050219726562508388 +1.479875 -0.40087890625 -0.422119140625 0.050219726562508388 +1.48 -0.40087890625 -0.422119140625 0.050219726562508388 +1.4801250000000002 -0.398193359375 -0.419281005859375 0.050219726562508388 +1.48025 -0.398193359375 -0.419281005859375 0.050219726562508388 +1.480375 -0.395477294921875 -0.416412353515625 0.050219726562508388 +1.4805 -0.3927001953125 -0.413482666015625 0.050219726562508388 +1.480625 -0.3927001953125 -0.413482666015625 0.050219726562508388 +1.48075 -0.389862060546875 -0.4105224609375 0.050219726562508388 +1.480875 -0.389862060546875 -0.4105224609375 0.050219726562508388 +1.481 -0.38702392578125 -0.407501220703125 0.050219726562508388 +1.481125 -0.384124755859375 -0.404449462890625 0.050219726562508388 +1.48125 -0.384124755859375 -0.404449462890625 0.050219726562508388 +1.481375 -0.38116455078125 -0.401336669921875 0.050219726562508388 +1.4815 -0.38116455078125 -0.401336669921875 0.050219726562508388 +1.481625 -0.378173828125 -0.398193359375 0.050219726562508388 +1.4817500000000002 -0.37518310546875 -0.39501953125 0.050219726562508388 +1.481875 -0.37518310546875 -0.39501953125 0.050219726562508388 +1.482 -0.372100830078125 -0.39178466796875 0.050219726562508388 +1.4821250000000002 -0.372100830078125 -0.39178466796875 0.050219726562508388 +1.48225 -0.368988037109375 -0.388519287109375 0.050219726562508388 +1.482375 -0.3658447265625 -0.38519287109375 0.050219726562508388 +1.4825 -0.3658447265625 -0.38519287109375 0.050219726562508388 +1.482625 -0.362640380859375 -0.3818359375 0.050219726562508388 +1.48275 -0.362640380859375 -0.3818359375 0.050219726562508388 +1.482875 -0.359405517578125 -0.378448486328125 0.050219726562508388 +1.483 -0.35614013671875 -0.375 0.050219726562508388 +1.483125 -0.35614013671875 -0.375 0.050219726562508388 +1.48325 -0.35284423828125 -0.37152099609375 0.050219726562508388 +1.483375 -0.35284423828125 -0.37152099609375 0.050219726562508388 +1.4835 -0.3494873046875 -0.36798095703125 0.050219726562508388 +1.483625 -0.346099853515625 -0.36444091796875 0.050219726562508388 +1.4837500000000002 -0.346099853515625 -0.36444091796875 0.050219726562508388 +1.483875 -0.342681884765625 -0.360809326171875 0.050219726562508388 +1.484 -0.342681884765625 -0.360809326171875 0.050219726562508388 +1.4841250000000002 -0.3392333984375 -0.357177734375 0.050219726562508388 +1.48425 -0.335723876953125 -0.353485107421875 0.050219726562508388 +1.484375 -0.335723876953125 -0.353485107421875 0.050219726562508388 +1.4845 -0.332183837890625 -0.349761962890625 0.050219726562508388 +1.484625 -0.332183837890625 -0.349761962890625 0.050219726562508388 +1.48475 -0.32861328125 -0.34600830078125 0.050219726562508388 +1.484875 -0.32501220703125 -0.34222412109375 0.050219726562508388 +1.485 -0.32501220703125 -0.34222412109375 0.050219726562508388 +1.485125 -0.321380615234375 -0.33837890625 0.050219726562508388 +1.48525 -0.321380615234375 -0.33837890625 0.050219726562508388 +1.4853750000000002 -0.31768798828125 -0.334503173828125 0.050219726562508388 +1.4855 -0.313995361328125 -0.330596923828125 0.050219726562508388 +1.485625 -0.313995361328125 -0.330596923828125 0.050219726562508388 +1.4857500000000002 -0.31024169921875 -0.32666015625 0.050219726562508388 +1.485875 -0.31024169921875 -0.32666015625 0.050219726562508388 +1.486 -0.30645751953125 -0.322662353515625 0.050219726562508388 +1.4861250000000002 -0.302642822265625 -0.31866455078125 0.050219726562508388 +1.48625 -0.302642822265625 -0.31866455078125 0.050219726562508388 +1.486375 -0.298797607421875 -0.314605712890625 0.050219726562508388 +1.4865 -0.298797607421875 -0.314605712890625 0.050219726562508388 +1.486625 -0.294921875 -0.310516357421875 0.050219726562508388 +1.48675 -0.291015625 -0.306396484375 0.050219726562508388 +1.486875 -0.291015625 -0.306396484375 0.050219726562508388 +1.487 -0.28704833984375 -0.30224609375 0.050219726562508388 +1.487125 -0.28704833984375 -0.30224609375 0.050219726562508388 +1.48725 -0.2830810546875 -0.298065185546875 0.050219726562508388 +1.4873750000000002 -0.279052734375 -0.2938232421875 0.050219726562508388 +1.4875 -0.279052734375 -0.2938232421875 0.050219726562508388 +1.487625 -0.2750244140625 -0.289581298828125 0.050219726562508388 +1.4877500000000002 -0.2750244140625 -0.289581298828125 0.050219726562508388 +1.487875 -0.270965576171875 -0.285308837890625 0.050219726562508388 +1.488 -0.266876220703125 -0.280975341796875 0.050219726562508388 +1.488125 -0.266876220703125 -0.280975341796875 0.050219726562508388 +1.48825 -0.262725830078125 -0.276641845703125 0.050219726562508388 +1.488375 -0.262725830078125 -0.276641845703125 0.050219726562508388 +1.4885 -0.258575439453125 -0.27227783203125 0.050219726562508388 +1.488625 -0.25439453125 -0.267852783203125 0.050219726562508388 +1.48875 -0.25439453125 -0.267852783203125 0.050219726562508388 +1.488875 -0.25018310546875 -0.263427734375 0.050219726562508388 +1.489 -0.25018310546875 -0.263427734375 0.050219726562508388 +1.489125 -0.245941162109375 -0.25897216796875 0.050219726562508388 +1.48925 -0.241668701171875 -0.25445556640625 0.050219726562508388 +1.4893750000000002 -0.241668701171875 -0.25445556640625 0.050219726562508388 +1.4895 -0.237396240234375 -0.249969482421875 0.050219726562508388 +1.489625 -0.237396240234375 -0.249969482421875 0.050219726562508388 +1.4897500000000002 -0.233062744140625 -0.24542236328125 0.050219726562508388 +1.489875 -0.228729248046875 -0.2408447265625 0.050219726562508388 +1.49 -0.228729248046875 -0.2408447265625 0.050219726562508388 +1.490125 -0.224365234375 -0.236236572265625 0.050219726562508388 +1.49025 -0.224365234375 -0.236236572265625 0.050219726562508388 +1.490375 -0.219970703125 -0.231597900390625 0.050219726562508388 +1.4905 -0.215545654296875 -0.226959228515625 0.050219726562508388 +1.490625 -0.215545654296875 -0.226959228515625 0.050219726562508388 +1.49075 -0.211090087890625 -0.2222900390625 0.050219726562508388 +1.490875 -0.211090087890625 -0.2222900390625 0.050219726562508388 +1.491 -0.206634521484375 -0.21759033203125 0.050219726562508388 +1.491125 -0.2021484375 -0.212860107421875 0.050219726562508388 +1.49125 -0.2021484375 -0.212860107421875 0.050219726562508388 +1.4913750000000002 -0.1976318359375 -0.208099365234375 0.050219726562508388 +1.4915 -0.1976318359375 -0.208099365234375 0.050219726562508388 +1.491625 -0.193115234375 -0.203338623046875 0.050219726562508388 +1.4917500000000002 -0.188568115234375 -0.19854736328125 0.050219726562508388 +1.491875 -0.188568115234375 -0.19854736328125 0.050219726562508388 +1.492 -0.183990478515625 -0.1937255859375 0.050219726562508388 +1.492125 -0.183990478515625 -0.1937255859375 0.050219726562508388 +1.49225 -0.17938232421875 -0.188873291015625 0.050219726562508388 +1.492375 -0.174774169921875 -0.18402099609375 0.050219726562508388 +1.4925 -0.174774169921875 -0.18402099609375 0.050219726562508388 +1.492625 -0.170135498046875 -0.17913818359375 0.050219726562508388 +1.49275 -0.170135498046875 -0.17913818359375 0.050219726562508388 +1.492875 -0.165496826171875 -0.17425537109375 0.050219726562508388 +1.493 -0.16082763671875 -0.169342041015625 0.050219726562508388 +1.493125 -0.16082763671875 -0.169342041015625 0.050219726562508388 +1.49325 -0.1561279296875 -0.164398193359375 0.050219726562508388 +1.4933750000000002 -0.1561279296875 -0.164398193359375 0.050219726562508388 +1.4935 -0.15142822265625 -0.159454345703125 0.050219726562508388 +1.493625 -0.146697998046875 -0.15447998046875 0.050219726562508388 +1.4937500000000002 -0.146697998046875 -0.15447998046875 0.050219726562508388 +1.493875 -0.1419677734375 -0.14947509765625 0.050219726562508388 +1.494 -0.1419677734375 -0.14947509765625 0.050219726562508388 +1.494125 -0.13720703125 -0.14447021484375 0.050219726562508388 +1.49425 -0.1324462890625 -0.13946533203125 0.050219726562508388 +1.494375 -0.1324462890625 -0.13946533203125 0.050219726562508388 +1.4945 -0.127655029296875 -0.134429931640625 0.050219726562508388 +1.494625 -0.127655029296875 -0.134429931640625 0.050219726562508388 +1.49475 -0.12286376953125 -0.129364013671875 0.050219726562508388 +1.494875 -0.1180419921875 -0.124298095703125 0.050219726562508388 +1.495 -0.1180419921875 -0.124298095703125 0.050219726562508388 +1.495125 -0.113250732421875 -0.119232177734375 0.050219726562508388 +1.49525 -0.113250732421875 -0.119232177734375 0.050219726562508388 +1.4953750000000002 -0.1083984375 -0.1141357421875 0.050219726562508388 +1.4955 -0.103546142578125 -0.109039306640625 0.050219726562508388 +1.495625 -0.103546142578125 -0.109039306640625 0.050219726562508388 +1.4957500000000002 -0.09869384765625 -0.103912353515625 0.050219726562508388 +1.495875 -0.09869384765625 -0.103912353515625 0.050219726562508388 +1.496 -0.093841552734375 -0.098785400390625 0.050219726562508388 +1.496125 -0.088958740234375 -0.093658447265625 0.050219726562508388 +1.49625 -0.088958740234375 -0.093658447265625 0.050219726562508388 +1.496375 -0.084075927734375 -0.0885009765625 0.050219726562508388 +1.4965 -0.084075927734375 -0.0885009765625 0.050219726562508388 +1.496625 -0.07916259765625 -0.083343505859375 0.050219726562508388 +1.49675 -0.074249267578125 -0.07818603515625 0.050219726562508388 +1.496875 -0.074249267578125 -0.07818603515625 0.050219726562508388 +1.497 -0.0693359375 -0.072998046875 0.050219726562508388 +1.497125 -0.0693359375 -0.072998046875 0.050219726562508388 +1.49725 -0.064422607421875 -0.06781005859375 0.050219726562508388 +1.4973750000000002 -0.059478759765625 -0.0626220703125 0.050219726562508388 +1.4975 -0.059478759765625 -0.0626220703125 0.050219726562508388 +1.497625 -0.0545654296875 -0.05743408203125 0.050219726562508388 +1.4977500000000002 -0.0545654296875 -0.05743408203125 0.050219726562508388 +1.497875 -0.04962158203125 -0.05224609375 0.050219726562508388 +1.498 -0.044647216796875 -0.047027587890625 0.050219726562508388 +1.498125 -0.044647216796875 -0.047027587890625 0.050219726562508388 +1.49825 -0.039703369140625 -0.04180908203125 0.050219726562508388 +1.498375 -0.039703369140625 -0.04180908203125 0.050219726562508388 +1.4985 -0.034759521484375 -0.036590576171875 0.050219726562508388 +1.498625 -0.02978515625 -0.0313720703125 0.050219726562508388 +1.49875 -0.02978515625 -0.0313720703125 0.050219726562508388 +1.498875 -0.024810791015625 -0.026123046875 0.050219726562508388 +1.499 -0.024810791015625 -0.026123046875 0.050219726562508388 +1.499125 -0.019866943359375 -0.020904541015625 0.050219726562508388 +1.49925 -0.014892578125 -0.01568603515625 0.050219726562508388 +1.4993750000000002 -0.014892578125 -0.01568603515625 0.050219726562508388 +1.4995 -0.009918212890625 -0.01043701171875 0.050219726562508388 +1.499625 -0.009918212890625 -0.01043701171875 0.050219726562508388 +1.4997500000000002 -0.00494384765625 -0.005218505859375 0.050219726562508388 +1.499875 0.0 0.0 0.050219726562508388 +1.5 0.0 0.0 0.050219726562508388 +1.500125 0.00494384765625 0.005218505859375 0.050219726562508388 +1.50025 0.00494384765625 0.005218505859375 0.050219726562508388 +1.500375 0.009918212890625 0.01043701171875 0.050219726562508388 +1.5005 0.014892578125 0.01568603515625 0.050219726562508388 +1.500625 0.014892578125 0.01568603515625 0.050219726562508388 +1.50075 0.019866943359375 0.020904541015625 0.050219726562508388 +1.500875 0.019866943359375 0.020904541015625 0.050219726562508388 +1.5010000000000002 0.024810791015625 0.026123046875 0.050219726562508388 +1.501125 0.02978515625 0.0313720703125 0.050219726562508388 +1.50125 0.02978515625 0.0313720703125 0.050219726562508388 +1.5013750000000002 0.034759521484375 0.036590576171875 0.050219726562508388 +1.5015 0.034759521484375 0.036590576171875 0.050219726562508388 +1.501625 0.039703369140625 0.04180908203125 0.050219726562508388 +1.5017500000000002 0.044647216796875 0.047027587890625 0.050219726562508388 +1.501875 0.044647216796875 0.047027587890625 0.050219726562508388 +1.502 0.04962158203125 0.05224609375 0.050219726562508388 +1.502125 0.04962158203125 0.05224609375 0.050219726562508388 +1.50225 0.0545654296875 0.05743408203125 0.050219726562508388 +1.502375 0.059478759765625 0.0626220703125 0.050219726562508388 +1.5025 0.059478759765625 0.0626220703125 0.050219726562508388 +1.502625 0.064422607421875 0.06781005859375 0.050219726562508388 +1.50275 0.064422607421875 0.06781005859375 0.050219726562508388 +1.502875 0.0693359375 0.072998046875 0.050219726562508388 +1.5030000000000002 0.074249267578125 0.07818603515625 0.050219726562508388 +1.503125 0.074249267578125 0.07818603515625 0.050219726562508388 +1.50325 0.07916259765625 0.083343505859375 0.050219726562508388 +1.5033750000000002 0.07916259765625 0.083343505859375 0.050219726562508388 +1.5035 0.084075927734375 0.0885009765625 0.050219726562508388 +1.503625 0.088958740234375 0.093658447265625 0.050219726562508388 +1.5037500000000002 0.088958740234375 0.093658447265625 0.050219726562508388 +1.503875 0.093841552734375 0.098785400390625 0.050219726562508388 +1.504 0.05560302734375 0.098785400390625 0.43709106445313256 +1.504125 0.0584716796875 0.103912353515625 0.43709106445313256 +1.50425 0.061370849609375 0.109039306640625 0.43709106445313256 +1.504375 0.061370849609375 0.109039306640625 0.43709106445313256 +1.5045 0.064239501953125 0.1141357421875 0.43709106445313256 +1.504625 0.064239501953125 0.1141357421875 0.43709106445313256 +1.50475 0.067108154296875 0.119232177734375 0.43709106445313256 +1.504875 0.0699462890625 0.124298095703125 0.43709106445313256 +1.5050000000000002 0.0699462890625 0.124298095703125 0.43709106445313256 +1.505125 0.07281494140625 0.129364013671875 0.43709106445313256 +1.50525 0.07281494140625 0.129364013671875 0.43709106445313256 +1.5053750000000002 0.075653076171875 0.134429931640625 0.43709106445313256 +1.5055 0.0784912109375 0.13946533203125 0.43709106445313256 +1.505625 0.0784912109375 0.13946533203125 0.43709106445313256 +1.50575 0.081298828125 0.14447021484375 0.43709106445313256 +1.505875 0.081298828125 0.14447021484375 0.43709106445313256 +1.506 0.084136962890625 0.14947509765625 0.43709106445313256 +1.506125 0.086944580078125 0.15447998046875 0.43709106445313256 +1.50625 0.086944580078125 0.15447998046875 0.43709106445313256 +1.506375 0.0897216796875 0.159454345703125 0.43709106445313256 +1.5065 0.0897216796875 0.159454345703125 0.43709106445313256 +1.506625 0.092529296875 0.164398193359375 0.43709106445313256 +1.50675 0.095306396484375 0.169342041015625 0.43709106445313256 +1.506875 0.095306396484375 0.169342041015625 0.43709106445313256 +1.5070000000000002 0.098052978515625 0.17425537109375 0.43709106445313256 +1.507125 0.098052978515625 0.17425537109375 0.43709106445313256 +1.50725 0.100830078125 0.17913818359375 0.43709106445313256 +1.5073750000000002 0.10357666015625 0.18402099609375 0.43709106445313256 +1.5075 0.10357666015625 0.18402099609375 0.43709106445313256 +1.507625 0.106292724609375 0.188873291015625 0.43709106445313256 +1.50775 0.106292724609375 0.188873291015625 0.43709106445313256 +1.507875 0.1090087890625 0.1937255859375 0.43709106445313256 +1.508 0.111724853515625 0.19854736328125 0.43709106445313256 +1.508125 0.111724853515625 0.19854736328125 0.43709106445313256 +1.50825 0.11444091796875 0.203338623046875 0.43709106445313256 +1.508375 0.11444091796875 0.203338623046875 0.43709106445313256 +1.5085 0.11712646484375 0.208099365234375 0.43709106445313256 +1.508625 0.119781494140625 0.212860107421875 0.43709106445313256 +1.50875 0.119781494140625 0.212860107421875 0.43709106445313256 +1.508875 0.122467041015625 0.21759033203125 0.43709106445313256 +1.5090000000000002 0.122467041015625 0.21759033203125 0.43709106445313256 +1.509125 0.125091552734375 0.2222900390625 0.43709106445313256 +1.50925 0.127716064453125 0.226959228515625 0.43709106445313256 +1.5093750000000002 0.127716064453125 0.226959228515625 0.43709106445313256 +1.5095 0.130340576171875 0.231597900390625 0.43709106445313256 +1.509625 0.130340576171875 0.231597900390625 0.43709106445313256 +1.50975 0.132965087890625 0.236236572265625 0.43709106445313256 +1.509875 0.135528564453125 0.2408447265625 0.43709106445313256 +1.51 0.135528564453125 0.2408447265625 0.43709106445313256 +1.510125 0.13812255859375 0.24542236328125 0.43709106445313256 +1.51025 0.13812255859375 0.24542236328125 0.43709106445313256 +1.510375 0.14068603515625 0.249969482421875 0.43709106445313256 +1.5105 0.143218994140625 0.25445556640625 0.43709106445313256 +1.510625 0.143218994140625 0.25445556640625 0.43709106445313256 +1.51075 0.145751953125 0.25897216796875 0.43709106445313256 +1.510875 0.145751953125 0.25897216796875 0.43709106445313256 +1.5110000000000002 0.14825439453125 0.263427734375 0.43709106445313256 +1.511125 0.1507568359375 0.267852783203125 0.43709106445313256 +1.51125 0.1507568359375 0.267852783203125 0.43709106445313256 +1.5113750000000002 0.153228759765625 0.27227783203125 0.43709106445313256 +1.5115 0.153228759765625 0.27227783203125 0.43709106445313256 +1.511625 0.15570068359375 0.276641845703125 0.43709106445313256 +1.51175 0.15814208984375 0.280975341796875 0.43709106445313256 +1.511875 0.15814208984375 0.280975341796875 0.43709106445313256 +1.512 0.16058349609375 0.285308837890625 0.43709106445313256 +1.512125 0.16058349609375 0.285308837890625 0.43709106445313256 +1.51225 0.162994384765625 0.289581298828125 0.43709106445313256 +1.512375 0.165374755859375 0.2938232421875 0.43709106445313256 +1.5125 0.165374755859375 0.2938232421875 0.43709106445313256 +1.512625 0.167755126953125 0.298065185546875 0.43709106445313256 +1.51275 0.167755126953125 0.298065185546875 0.43709106445313256 +1.512875 0.17010498046875 0.30224609375 0.43709106445313256 +1.5130000000000002 0.172454833984375 0.306396484375 0.43709106445313256 +1.513125 0.172454833984375 0.306396484375 0.43709106445313256 +1.51325 0.174774169921875 0.310516357421875 0.43709106445313256 +1.5133750000000002 0.174774169921875 0.310516357421875 0.43709106445313256 +1.5135 0.17706298828125 0.314605712890625 0.43709106445313256 +1.513625 0.179351806640625 0.31866455078125 0.43709106445313256 +1.51375 0.179351806640625 0.31866455078125 0.43709106445313256 +1.513875 0.181610107421875 0.322662353515625 0.43709106445313256 +1.514 0.181610107421875 0.322662353515625 0.43709106445313256 +1.514125 0.183837890625 0.32666015625 0.43709106445313256 +1.51425 0.186065673828125 0.330596923828125 0.43709106445313256 +1.514375 0.186065673828125 0.330596923828125 0.43709106445313256 +1.5145 0.188262939453125 0.334503173828125 0.43709106445313256 +1.514625 0.188262939453125 0.334503173828125 0.43709106445313256 +1.51475 0.190460205078125 0.33837890625 0.43709106445313256 +1.514875 0.192596435546875 0.34222412109375 0.43709106445313256 +1.5150000000000002 0.192596435546875 0.34222412109375 0.43709106445313256 +1.515125 0.194732666015625 0.34600830078125 0.43709106445313256 +1.51525 0.194732666015625 0.34600830078125 0.43709106445313256 +1.5153750000000002 0.196868896484375 0.349761962890625 0.43709106445313256 +1.5155 0.198944091796875 0.353485107421875 0.43709106445313256 +1.515625 0.198944091796875 0.353485107421875 0.43709106445313256 +1.51575 0.201019287109375 0.357177734375 0.43709106445313256 +1.515875 0.201019287109375 0.357177734375 0.43709106445313256 +1.516 0.20306396484375 0.360809326171875 0.43709106445313256 +1.516125 0.205108642578125 0.36444091796875 0.43709106445313256 +1.51625 0.205108642578125 0.36444091796875 0.43709106445313256 +1.516375 0.207122802734375 0.36798095703125 0.43709106445313256 +1.5165 0.207122802734375 0.36798095703125 0.43709106445313256 +1.5166250000000002 0.2091064453125 0.37152099609375 0.43709106445313256 +1.51675 0.2110595703125 0.375 0.43709106445313256 +1.516875 0.2110595703125 0.375 0.43709106445313256 +1.5170000000000002 0.212982177734375 0.378448486328125 0.43709106445313256 +1.517125 0.212982177734375 0.378448486328125 0.43709106445313256 +1.51725 0.21490478515625 0.3818359375 0.43709106445313256 +1.5173750000000002 0.216796875 0.38519287109375 0.43709106445313256 +1.5175 0.216796875 0.38519287109375 0.43709106445313256 +1.517625 0.218658447265625 0.388519287109375 0.43709106445313256 +1.51775 0.218658447265625 0.388519287109375 0.43709106445313256 +1.517875 0.22052001953125 0.39178466796875 0.43709106445313256 +1.518 0.222320556640625 0.39501953125 0.43709106445313256 +1.518125 0.222320556640625 0.39501953125 0.43709106445313256 +1.51825 0.22412109375 0.398193359375 0.43709106445313256 +1.518375 0.22412109375 0.398193359375 0.43709106445313256 +1.5185 0.22589111328125 0.401336669921875 0.43709106445313256 +1.5186250000000002 0.227630615234375 0.404449462890625 0.43709106445313256 +1.51875 0.227630615234375 0.404449462890625 0.43709106445313256 +1.518875 0.229339599609375 0.407501220703125 0.43709106445313256 +1.5190000000000002 0.229339599609375 0.407501220703125 0.43709106445313256 +1.519125 0.231048583984375 0.4105224609375 0.43709106445313256 +1.51925 0.23272705078125 0.413482666015625 0.43709106445313256 +1.5193750000000002 0.23272705078125 0.413482666015625 0.43709106445313256 +1.5195 0.234375 0.416412353515625 0.43709106445313256 +1.519625 0.234375 0.416412353515625 0.43709106445313256 +1.51975 0.2359619140625 0.419281005859375 0.43709106445313256 +1.519875 0.237579345703125 0.422119140625 0.43709106445313256 +1.52 0.237579345703125 0.422119140625 0.43709106445313256 +1.520125 0.2391357421875 0.424896240234375 0.43709106445313256 +1.52025 0.2391357421875 0.424896240234375 0.43709106445313256 +1.520375 0.24066162109375 0.4276123046875 0.43709106445313256 +1.5205 0.2421875 0.4302978515625 0.43709106445313256 +1.5206250000000002 0.2421875 0.4302978515625 0.43709106445313256 +1.52075 0.243682861328125 0.432952880859375 0.43709106445313256 +1.520875 0.243682861328125 0.432952880859375 0.43709106445313256 +1.5210000000000002 0.245147705078125 0.435546875 0.43709106445313256 +1.521125 0.246551513671875 0.438079833984375 0.43709106445313256 +1.52125 0.246551513671875 0.438079833984375 0.43709106445313256 +1.521375 0.24798583984375 0.440582275390625 0.43709106445313256 +1.5215 0.24798583984375 0.440582275390625 0.43709106445313256 +1.521625 0.249359130859375 0.44305419921875 0.43709106445313256 +1.52175 0.250701904296875 0.4454345703125 0.43709106445313256 +1.521875 0.250701904296875 0.4454345703125 0.43709106445313256 +1.522 0.25201416015625 0.447784423828125 0.43709106445313256 +1.522125 0.25201416015625 0.447784423828125 0.43709106445313256 +1.52225 0.253326416015625 0.450103759765625 0.43709106445313256 +1.522375 0.254608154296875 0.452362060546875 0.43709106445313256 +1.5225 0.254608154296875 0.452362060546875 0.43709106445313256 +1.5226250000000002 0.255828857421875 0.454559326171875 0.43709106445313256 +1.52275 0.255828857421875 0.454559326171875 0.43709106445313256 +1.522875 0.257049560546875 0.45672607421875 0.43709106445313256 +1.5230000000000002 0.25823974609375 0.458831787109375 0.43709106445313256 +1.523125 0.25823974609375 0.458831787109375 0.43709106445313256 +1.52325 0.2593994140625 0.46087646484375 0.43709106445313256 +1.523375 0.2593994140625 0.46087646484375 0.43709106445313256 +1.5235 0.260528564453125 0.462890625 0.43709106445313256 +1.523625 0.2615966796875 0.464813232421875 0.43709106445313256 +1.52375 0.2615966796875 0.464813232421875 0.43709106445313256 +1.523875 0.2626953125 0.46673583984375 0.43709106445313256 +1.524 0.2626953125 0.46673583984375 0.43709106445313256 +1.524125 0.26373291015625 0.46856689453125 0.43709106445313256 +1.52425 0.264739990234375 0.470367431640625 0.43709106445313256 +1.524375 0.264739990234375 0.470367431640625 0.43709106445313256 +1.5245 0.265716552734375 0.472137451171875 0.43709106445313256 +1.5246250000000002 0.265716552734375 0.472137451171875 0.43709106445313256 +1.52475 0.26666259765625 0.47381591796875 0.43709106445313256 +1.524875 0.267608642578125 0.4754638671875 0.43709106445313256 +1.5250000000000002 0.267608642578125 0.4754638671875 0.43709106445313256 +1.525125 0.26849365234375 0.47705078125 0.43709106445313256 +1.52525 0.26849365234375 0.47705078125 0.43709106445313256 +1.525375 0.269378662109375 0.478607177734375 0.43709106445313256 +1.5255 0.27020263671875 0.480072021484375 0.43709106445313256 +1.525625 0.27020263671875 0.480072021484375 0.43709106445313256 +1.52575 0.27099609375 0.48150634765625 0.43709106445313256 +1.525875 0.27099609375 0.48150634765625 0.43709106445313256 +1.526 0.27178955078125 0.48291015625 0.43709106445313256 +1.526125 0.27252197265625 0.484222412109375 0.43709106445313256 +1.52625 0.27252197265625 0.484222412109375 0.43709106445313256 +1.526375 0.27325439453125 0.485504150390625 0.43709106445313256 +1.5265 0.27325439453125 0.485504150390625 0.43709106445313256 +1.5266250000000002 0.273956298828125 0.486724853515625 0.43709106445313256 +1.52675 0.27459716796875 0.487884521484375 0.43709106445313256 +1.526875 0.27459716796875 0.487884521484375 0.43709106445313256 +1.5270000000000002 0.27520751953125 0.489013671875 0.43709106445313256 +1.527125 0.27520751953125 0.489013671875 0.43709106445313256 +1.52725 0.27581787109375 0.490081787109375 0.43709106445313256 +1.527375 0.276397705078125 0.4910888671875 0.43709106445313256 +1.5275 0.276397705078125 0.4910888671875 0.43709106445313256 +1.527625 0.27691650390625 0.492034912109375 0.43709106445313256 +1.52775 0.27691650390625 0.492034912109375 0.43709106445313256 +1.527875 0.277435302734375 0.492950439453125 0.43709106445313256 +1.528 0.277923583984375 0.4937744140625 0.43709106445313256 +1.528125 0.277923583984375 0.4937744140625 0.43709106445313256 +1.52825 0.278350830078125 0.49456787109375 0.43709106445313256 +1.528375 0.278350830078125 0.49456787109375 0.43709106445313256 +1.5285 0.278778076171875 0.49530029296875 0.43709106445313256 +1.5286250000000002 0.279144287109375 0.496002197265625 0.43709106445313256 +1.52875 0.279144287109375 0.496002197265625 0.43709106445313256 +1.528875 0.279510498046875 0.496612548828125 0.43709106445313256 +1.5290000000000002 0.279510498046875 0.496612548828125 0.43709106445313256 +1.529125 0.27984619140625 0.4971923828125 0.43709106445313256 +1.52925 0.280120849609375 0.497711181640625 0.43709106445313256 +1.529375 0.280120849609375 0.497711181640625 0.43709106445313256 +1.5295 0.2803955078125 0.498199462890625 0.43709106445313256 +1.529625 0.2803955078125 0.498199462890625 0.43709106445313256 +1.52975 0.280609130859375 0.49859619140625 0.43709106445313256 +1.529875 0.28082275390625 0.49896240234375 0.43709106445313256 +1.53 0.28082275390625 0.49896240234375 0.43709106445313256 +1.530125 0.281005859375 0.499267578125 0.43709106445313256 +1.53025 0.281005859375 0.499267578125 0.43709106445313256 +1.530375 0.2811279296875 0.49951171875 0.43709106445313256 +1.5305 0.281219482421875 0.49969482421875 0.43709106445313256 +1.5306250000000002 0.281219482421875 0.49969482421875 0.43709106445313256 +1.53075 0.28131103515625 0.49981689453125 0.43709106445313256 +1.530875 0.28131103515625 0.49981689453125 0.43709106445313256 +1.5310000000000002 0.2813720703125 0.499908447265625 0.43709106445313256 +1.531125 0.2813720703125 0.49993896484375 0.43709106445313256 +1.53125 0.2813720703125 0.49993896484375 0.43709106445313256 +1.531375 0.2813720703125 0.499908447265625 0.43709106445313256 +1.5315 0.2813720703125 0.499908447265625 0.43709106445313256 +1.531625 0.28131103515625 0.49981689453125 0.43709106445313256 +1.53175 0.281219482421875 0.49969482421875 0.43709106445313256 +1.531875 0.281219482421875 0.49969482421875 0.43709106445313256 +1.532 0.2811279296875 0.49951171875 0.43709106445313256 +1.532125 0.2811279296875 0.49951171875 0.43709106445313256 +1.5322500000000002 0.281005859375 0.499267578125 0.43709106445313256 +1.532375 0.28082275390625 0.49896240234375 0.43709106445313256 +1.5325 0.28082275390625 0.49896240234375 0.43709106445313256 +1.5326250000000002 0.280609130859375 0.49859619140625 0.43709106445313256 +1.53275 0.280609130859375 0.49859619140625 0.43709106445313256 +1.532875 0.2803955078125 0.498199462890625 0.43709106445313256 +1.5330000000000002 0.280120849609375 0.497711181640625 0.43709106445313256 +1.533125 0.280120849609375 0.497711181640625 0.43709106445313256 +1.53325 0.27984619140625 0.4971923828125 0.43709106445313256 +1.533375 0.27984619140625 0.4971923828125 0.43709106445313256 +1.5335 0.279510498046875 0.496612548828125 0.43709106445313256 +1.533625 0.279144287109375 0.496002197265625 0.43709106445313256 +1.53375 0.279144287109375 0.496002197265625 0.43709106445313256 +1.533875 0.278778076171875 0.49530029296875 0.43709106445313256 +1.534 0.278778076171875 0.49530029296875 0.43709106445313256 +1.534125 0.278350830078125 0.49456787109375 0.43709106445313256 +1.5342500000000002 0.277923583984375 0.4937744140625 0.43709106445313256 +1.534375 0.277923583984375 0.4937744140625 0.43709106445313256 +1.5345 0.277435302734375 0.492950439453125 0.43709106445313256 +1.5346250000000002 0.277435302734375 0.492950439453125 0.43709106445313256 +1.53475 0.27691650390625 0.492034912109375 0.43709106445313256 +1.534875 0.276397705078125 0.4910888671875 0.43709106445313256 +1.5350000000000002 0.276397705078125 0.4910888671875 0.43709106445313256 +1.535125 0.27581787109375 0.490081787109375 0.43709106445313256 +1.53525 0.27581787109375 0.490081787109375 0.43709106445313256 +1.535375 0.27520751953125 0.489013671875 0.43709106445313256 +1.5355 0.27459716796875 0.487884521484375 0.43709106445313256 +1.535625 0.27459716796875 0.487884521484375 0.43709106445313256 +1.53575 0.273956298828125 0.486724853515625 0.43709106445313256 +1.535875 0.273956298828125 0.486724853515625 0.43709106445313256 +1.536 0.119293212890625 0.485504150390625 0.7541992187500055 +1.536125 0.11895751953125 0.484222412109375 0.7541992187500055 +1.5362500000000002 0.11895751953125 0.484222412109375 0.7541992187500055 +1.536375 0.11865234375 0.48291015625 0.7541992187500055 +1.5365 0.11865234375 0.48291015625 0.7541992187500055 +1.5366250000000002 0.1182861328125 0.48150634765625 0.7541992187500055 +1.53675 0.117950439453125 0.480072021484375 0.7541992187500055 +1.536875 0.117950439453125 0.480072021484375 0.7541992187500055 +1.537 0.117584228515625 0.478607177734375 0.7541992187500055 +1.537125 0.117584228515625 0.478607177734375 0.7541992187500055 +1.53725 0.117218017578125 0.47705078125 0.7541992187500055 +1.537375 0.1168212890625 0.4754638671875 0.7541992187500055 +1.5375 0.1168212890625 0.4754638671875 0.7541992187500055 +1.537625 0.116424560546875 0.47381591796875 0.7541992187500055 +1.53775 0.116424560546875 0.47381591796875 0.7541992187500055 +1.537875 0.115997314453125 0.472137451171875 0.7541992187500055 +1.538 0.115570068359375 0.470367431640625 0.7541992187500055 +1.538125 0.115570068359375 0.470367431640625 0.7541992187500055 +1.5382500000000002 0.1151123046875 0.46856689453125 0.7541992187500055 +1.538375 0.1151123046875 0.46856689453125 0.7541992187500055 +1.5385 0.114654541015625 0.46673583984375 0.7541992187500055 +1.5386250000000002 0.11419677734375 0.464813232421875 0.7541992187500055 +1.53875 0.11419677734375 0.464813232421875 0.7541992187500055 +1.538875 0.11370849609375 0.462890625 0.7541992187500055 +1.539 0.11370849609375 0.462890625 0.7541992187500055 +1.539125 0.11322021484375 0.46087646484375 0.7541992187500055 +1.53925 0.11273193359375 0.458831787109375 0.7541992187500055 +1.539375 0.11273193359375 0.458831787109375 0.7541992187500055 +1.5395 0.112213134765625 0.45672607421875 0.7541992187500055 +1.539625 0.112213134765625 0.45672607421875 0.7541992187500055 +1.53975 0.111663818359375 0.454559326171875 0.7541992187500055 +1.539875 0.11114501953125 0.452362060546875 0.7541992187500055 +1.54 0.11114501953125 0.452362060546875 0.7541992187500055 +1.540125 0.110595703125 0.450103759765625 0.7541992187500055 +1.5402500000000002 0.110595703125 0.450103759765625 0.7541992187500055 +1.540375 0.110015869140625 0.447784423828125 0.7541992187500055 +1.5405 0.10943603515625 0.4454345703125 0.7541992187500055 +1.5406250000000002 0.10943603515625 0.4454345703125 0.7541992187500055 +1.54075 0.108856201171875 0.44305419921875 0.7541992187500055 +1.540875 0.108856201171875 0.44305419921875 0.7541992187500055 +1.541 0.108245849609375 0.440582275390625 0.7541992187500055 +1.541125 0.107635498046875 0.438079833984375 0.7541992187500055 +1.54125 0.107635498046875 0.438079833984375 0.7541992187500055 +1.541375 0.10699462890625 0.435546875 0.7541992187500055 +1.5415 0.10699462890625 0.435546875 0.7541992187500055 +1.541625 0.10638427734375 0.432952880859375 0.7541992187500055 +1.54175 0.105712890625 0.4302978515625 0.7541992187500055 +1.541875 0.105712890625 0.4302978515625 0.7541992187500055 +1.542 0.105072021484375 0.4276123046875 0.7541992187500055 +1.542125 0.105072021484375 0.4276123046875 0.7541992187500055 +1.5422500000000002 0.104400634765625 0.424896240234375 0.7541992187500055 +1.542375 0.10369873046875 0.422119140625 0.7541992187500055 +1.5425 0.10369873046875 0.422119140625 0.7541992187500055 +1.5426250000000002 0.102996826171875 0.419281005859375 0.7541992187500055 +1.54275 0.102996826171875 0.419281005859375 0.7541992187500055 +1.542875 0.102294921875 0.416412353515625 0.7541992187500055 +1.543 0.101593017578125 0.413482666015625 0.7541992187500055 +1.543125 0.101593017578125 0.413482666015625 0.7541992187500055 +1.54325 0.100860595703125 0.4105224609375 0.7541992187500055 +1.543375 0.100860595703125 0.4105224609375 0.7541992187500055 +1.5435 0.100128173828125 0.407501220703125 0.7541992187500055 +1.543625 0.099365234375 0.404449462890625 0.7541992187500055 +1.54375 0.099365234375 0.404449462890625 0.7541992187500055 +1.543875 0.098602294921875 0.401336669921875 0.7541992187500055 +1.544 0.098602294921875 0.401336669921875 0.7541992187500055 +1.544125 0.09783935546875 0.398193359375 0.7541992187500055 +1.5442500000000002 0.0970458984375 0.39501953125 0.7541992187500055 +1.544375 0.0970458984375 0.39501953125 0.7541992187500055 +1.5445 0.09625244140625 0.39178466796875 0.7541992187500055 +1.5446250000000002 0.09625244140625 0.39178466796875 0.7541992187500055 +1.54475 0.095458984375 0.388519287109375 0.7541992187500055 +1.544875 0.094635009765625 0.38519287109375 0.7541992187500055 +1.545 0.094635009765625 0.38519287109375 0.7541992187500055 +1.545125 0.09381103515625 0.3818359375 0.7541992187500055 +1.54525 0.09381103515625 0.3818359375 0.7541992187500055 +1.545375 0.092987060546875 0.378448486328125 0.7541992187500055 +1.5455 0.092132568359375 0.375 0.7541992187500055 +1.545625 0.092132568359375 0.375 0.7541992187500055 +1.54575 0.091278076171875 0.37152099609375 0.7541992187500055 +1.545875 0.091278076171875 0.37152099609375 0.7541992187500055 +1.546 0.09039306640625 0.36798095703125 0.7541992187500055 +1.546125 0.08953857421875 0.36444091796875 0.7541992187500055 +1.5462500000000002 0.08953857421875 0.36444091796875 0.7541992187500055 +1.546375 0.088653564453125 0.360809326171875 0.7541992187500055 +1.5465 0.088653564453125 0.360809326171875 0.7541992187500055 +1.5466250000000002 0.0877685546875 0.357177734375 0.7541992187500055 +1.54675 0.08685302734375 0.353485107421875 0.7541992187500055 +1.546875 0.08685302734375 0.353485107421875 0.7541992187500055 +1.547 0.0859375 0.349761962890625 0.7541992187500055 +1.547125 0.0859375 0.349761962890625 0.7541992187500055 +1.54725 0.08502197265625 0.34600830078125 0.7541992187500055 +1.547375 0.084075927734375 0.34222412109375 0.7541992187500055 +1.5475 0.084075927734375 0.34222412109375 0.7541992187500055 +1.547625 0.0831298828125 0.33837890625 0.7541992187500055 +1.54775 0.0831298828125 0.33837890625 0.7541992187500055 +1.5478750000000002 0.082183837890625 0.334503173828125 0.7541992187500055 +1.548 0.08123779296875 0.330596923828125 0.7541992187500055 +1.548125 0.08123779296875 0.330596923828125 0.7541992187500055 +1.5482500000000002 0.08026123046875 0.32666015625 0.7541992187500055 +1.548375 0.08026123046875 0.32666015625 0.7541992187500055 +1.5485 0.07928466796875 0.322662353515625 0.7541992187500055 +1.5486250000000002 0.078277587890625 0.31866455078125 0.7541992187500055 +1.54875 0.078277587890625 0.31866455078125 0.7541992187500055 +1.548875 0.077301025390625 0.314605712890625 0.7541992187500055 +1.549 0.077301025390625 0.314605712890625 0.7541992187500055 +1.549125 0.0762939453125 0.310516357421875 0.7541992187500055 +1.54925 0.075286865234375 0.306396484375 0.7541992187500055 +1.549375 0.075286865234375 0.306396484375 0.7541992187500055 +1.5495 0.074249267578125 0.30224609375 0.7541992187500055 +1.549625 0.074249267578125 0.30224609375 0.7541992187500055 +1.54975 0.073211669921875 0.298065185546875 0.7541992187500055 +1.5498750000000002 0.072174072265625 0.2938232421875 0.7541992187500055 +1.55 0.072174072265625 0.2938232421875 0.7541992187500055 +1.550125 0.071136474609375 0.289581298828125 0.7541992187500055 +1.5502500000000002 0.071136474609375 0.289581298828125 0.7541992187500055 +1.550375 0.070098876953125 0.285308837890625 0.7541992187500055 +1.5505 0.06903076171875 0.280975341796875 0.7541992187500055 +1.5506250000000002 0.06903076171875 0.280975341796875 0.7541992187500055 +1.55075 0.067962646484375 0.276641845703125 0.7541992187500055 +1.550875 0.067962646484375 0.276641845703125 0.7541992187500055 +1.551 0.06689453125 0.27227783203125 0.7541992187500055 +1.551125 0.0657958984375 0.267852783203125 0.7541992187500055 +1.55125 0.0657958984375 0.267852783203125 0.7541992187500055 +1.551375 0.064727783203125 0.263427734375 0.7541992187500055 +1.5515 0.064727783203125 0.263427734375 0.7541992187500055 +1.551625 0.063629150390625 0.25897216796875 0.7541992187500055 +1.55175 0.0625 0.25445556640625 0.7541992187500055 +1.5518750000000002 0.0625 0.25445556640625 0.7541992187500055 +1.552 0.0614013671875 0.249969482421875 0.7541992187500055 +1.552125 0.0614013671875 0.249969482421875 0.7541992187500055 +1.5522500000000002 0.060302734375 0.24542236328125 0.7541992187500055 +1.552375 0.059173583984375 0.2408447265625 0.7541992187500055 +1.5525 0.059173583984375 0.2408447265625 0.7541992187500055 +1.552625 0.05804443359375 0.236236572265625 0.7541992187500055 +1.55275 0.05804443359375 0.236236572265625 0.7541992187500055 +1.552875 0.056884765625 0.231597900390625 0.7541992187500055 +1.553 0.055755615234375 0.226959228515625 0.7541992187500055 +1.553125 0.055755615234375 0.226959228515625 0.7541992187500055 +1.55325 0.054595947265625 0.2222900390625 0.7541992187500055 +1.553375 0.054595947265625 0.2222900390625 0.7541992187500055 +1.5535 0.053436279296875 0.21759033203125 0.7541992187500055 +1.553625 0.052276611328125 0.212860107421875 0.7541992187500055 +1.55375 0.052276611328125 0.212860107421875 0.7541992187500055 +1.5538750000000002 0.051116943359375 0.208099365234375 0.7541992187500055 +1.554 0.051116943359375 0.208099365234375 0.7541992187500055 +1.554125 0.049957275390625 0.203338623046875 0.7541992187500055 +1.5542500000000002 0.04876708984375 0.19854736328125 0.7541992187500055 +1.554375 0.04876708984375 0.19854736328125 0.7541992187500055 +1.5545 0.047576904296875 0.1937255859375 0.7541992187500055 +1.554625 0.047576904296875 0.1937255859375 0.7541992187500055 +1.55475 0.04638671875 0.188873291015625 0.7541992187500055 +1.554875 0.045196533203125 0.18402099609375 0.7541992187500055 +1.555 0.045196533203125 0.18402099609375 0.7541992187500055 +1.555125 0.04400634765625 0.17913818359375 0.7541992187500055 +1.55525 0.04400634765625 0.17913818359375 0.7541992187500055 +1.555375 0.042816162109375 0.17425537109375 0.7541992187500055 +1.5555 0.041595458984375 0.169342041015625 0.7541992187500055 +1.555625 0.041595458984375 0.169342041015625 0.7541992187500055 +1.55575 0.040374755859375 0.164398193359375 0.7541992187500055 +1.5558750000000002 0.040374755859375 0.164398193359375 0.7541992187500055 +1.556 0.039154052734375 0.159454345703125 0.7541992187500055 +1.556125 0.037933349609375 0.15447998046875 0.7541992187500055 +1.5562500000000002 0.037933349609375 0.15447998046875 0.7541992187500055 +1.556375 0.036712646484375 0.14947509765625 0.7541992187500055 +1.5565 0.036712646484375 0.14947509765625 0.7541992187500055 +1.556625 0.035491943359375 0.14447021484375 0.7541992187500055 +1.55675 0.03424072265625 0.13946533203125 0.7541992187500055 +1.556875 0.03424072265625 0.13946533203125 0.7541992187500055 +1.557 0.03302001953125 0.134429931640625 0.7541992187500055 +1.557125 0.03302001953125 0.134429931640625 0.7541992187500055 +1.55725 0.031768798828125 0.129364013671875 0.7541992187500055 +1.557375 0.030517578125 0.124298095703125 0.7541992187500055 +1.5575 0.030517578125 0.124298095703125 0.7541992187500055 +1.557625 0.029296875 0.119232177734375 0.7541992187500055 +1.55775 0.029296875 0.119232177734375 0.7541992187500055 +1.5578750000000002 0.028045654296875 0.1141357421875 0.7541992187500055 +1.558 0.026763916015625 0.109039306640625 0.7541992187500055 +1.558125 0.026763916015625 0.109039306640625 0.7541992187500055 +1.5582500000000002 0.0255126953125 0.103912353515625 0.7541992187500055 +1.558375 0.0255126953125 0.103912353515625 0.7541992187500055 +1.5585 0.024261474609375 0.098785400390625 0.7541992187500055 +1.558625 0.02301025390625 0.093658447265625 0.7541992187500055 +1.55875 0.02301025390625 0.093658447265625 0.7541992187500055 +1.558875 0.021728515625 0.0885009765625 0.7541992187500055 +1.559 0.021728515625 0.0885009765625 0.7541992187500055 +1.559125 0.020477294921875 0.083343505859375 0.7541992187500055 +1.55925 0.019195556640625 0.07818603515625 0.7541992187500055 +1.559375 0.019195556640625 0.07818603515625 0.7541992187500055 +1.5595 0.017913818359375 0.072998046875 0.7541992187500055 +1.559625 0.017913818359375 0.072998046875 0.7541992187500055 +1.55975 0.01666259765625 0.06781005859375 0.7541992187500055 +1.5598750000000002 0.015380859375 0.0626220703125 0.7541992187500055 +1.56 0.015380859375 0.0626220703125 0.7541992187500055 +1.560125 0.01409912109375 0.05743408203125 0.7541992187500055 +1.5602500000000002 0.01409912109375 0.05743408203125 0.7541992187500055 +1.560375 0.0128173828125 0.05224609375 0.7541992187500055 +1.5605 0.01153564453125 0.047027587890625 0.7541992187500055 +1.560625 0.01153564453125 0.047027587890625 0.7541992187500055 +1.56075 0.01025390625 0.04180908203125 0.7541992187500055 +1.560875 0.01025390625 0.04180908203125 0.7541992187500055 +1.561 0.00897216796875 0.036590576171875 0.7541992187500055 +1.561125 0.0076904296875 0.0313720703125 0.7541992187500055 +1.56125 0.0076904296875 0.0313720703125 0.7541992187500055 +1.561375 0.00640869140625 0.026123046875 0.7541992187500055 +1.5615 0.00640869140625 0.026123046875 0.7541992187500055 +1.561625 0.005126953125 0.020904541015625 0.7541992187500055 +1.56175 0.00384521484375 0.01568603515625 0.7541992187500055 +1.5618750000000002 0.00384521484375 0.01568603515625 0.7541992187500055 +1.562 0.0025634765625 0.01043701171875 0.7541992187500055 +1.562125 0.0025634765625 0.01043701171875 0.7541992187500055 +1.5622500000000002 0.00128173828125 0.005218505859375 0.7541992187500055 +1.562375 0.0 0.0 0.7541992187500055 +1.5625 0.0 0.0 0.7541992187500055 +1.562625 -0.00128173828125 -0.005218505859375 0.7541992187500055 +1.56275 -0.00128173828125 -0.005218505859375 0.7541992187500055 +1.562875 -0.0025634765625 -0.01043701171875 0.7541992187500055 +1.563 -0.00384521484375 -0.01568603515625 0.7541992187500055 +1.563125 -0.00384521484375 -0.01568603515625 0.7541992187500055 +1.56325 -0.005126953125 -0.020904541015625 0.7541992187500055 +1.563375 -0.005126953125 -0.020904541015625 0.7541992187500055 +1.5635000000000002 -0.00640869140625 -0.026123046875 0.7541992187500055 +1.563625 -0.0076904296875 -0.0313720703125 0.7541992187500055 +1.56375 -0.0076904296875 -0.0313720703125 0.7541992187500055 +1.5638750000000002 -0.00897216796875 -0.036590576171875 0.7541992187500055 +1.564 -0.00897216796875 -0.036590576171875 0.7541992187500055 +1.564125 -0.01025390625 -0.04180908203125 0.7541992187500055 +1.5642500000000002 -0.01153564453125 -0.047027587890625 0.7541992187500055 +1.564375 -0.01153564453125 -0.047027587890625 0.7541992187500055 +1.5645 -0.0128173828125 -0.05224609375 0.7541992187500055 +1.564625 -0.0128173828125 -0.05224609375 0.7541992187500055 +1.56475 -0.01409912109375 -0.05743408203125 0.7541992187500055 +1.564875 -0.015380859375 -0.0626220703125 0.7541992187500055 +1.565 -0.015380859375 -0.0626220703125 0.7541992187500055 +1.565125 -0.01666259765625 -0.06781005859375 0.7541992187500055 +1.56525 -0.01666259765625 -0.06781005859375 0.7541992187500055 +1.565375 -0.017913818359375 -0.072998046875 0.7541992187500055 +1.5655000000000002 -0.019195556640625 -0.07818603515625 0.7541992187500055 +1.565625 -0.019195556640625 -0.07818603515625 0.7541992187500055 +1.56575 -0.020477294921875 -0.083343505859375 0.7541992187500055 +1.5658750000000002 -0.020477294921875 -0.083343505859375 0.7541992187500055 +1.566 -0.021728515625 -0.0885009765625 0.7541992187500055 +1.566125 -0.02301025390625 -0.093658447265625 0.7541992187500055 +1.5662500000000002 -0.02301025390625 -0.093658447265625 0.7541992187500055 +1.566375 -0.024261474609375 -0.098785400390625 0.7541992187500055 +1.5665 -0.024261474609375 -0.098785400390625 0.7541992187500055 +1.566625 -0.0255126953125 -0.103912353515625 0.7541992187500055 +1.56675 -0.026763916015625 -0.109039306640625 0.7541992187500055 +1.566875 -0.026763916015625 -0.109039306640625 0.7541992187500055 +1.567 -0.028045654296875 -0.1141357421875 0.7541992187500055 +1.567125 -0.028045654296875 -0.1141357421875 0.7541992187500055 +1.56725 -0.029296875 -0.119232177734375 0.7541992187500055 +1.567375 -0.030517578125 -0.124298095703125 0.7541992187500055 +1.5675000000000002 -0.030517578125 -0.124298095703125 0.7541992187500055 +1.567625 -0.031768798828125 -0.129364013671875 0.7541992187500055 +1.56775 -0.031768798828125 -0.129364013671875 0.7541992187500055 +1.5678750000000002 -0.03302001953125 -0.134429931640625 0.7541992187500055 +1.568 -0.006805419921875 -0.13946533203125 0.9510192871093776 +1.568125 -0.006805419921875 -0.13946533203125 0.9510192871093776 +1.56825 -0.007049560546875 -0.14447021484375 0.9510192871093776 +1.568375 -0.007049560546875 -0.14447021484375 0.9510192871093776 +1.5685 -0.007293701171875 -0.14947509765625 0.9510192871093776 +1.568625 -0.007537841796875 -0.15447998046875 0.9510192871093776 +1.56875 -0.007537841796875 -0.15447998046875 0.9510192871093776 +1.568875 -0.007781982421875 -0.159454345703125 0.9510192871093776 +1.569 -0.007781982421875 -0.159454345703125 0.9510192871093776 +1.569125 -0.008026123046875 -0.164398193359375 0.9510192871093776 +1.56925 -0.008270263671875 -0.169342041015625 0.9510192871093776 +1.569375 -0.008270263671875 -0.169342041015625 0.9510192871093776 +1.5695000000000002 -0.008514404296875 -0.17425537109375 0.9510192871093776 +1.569625 -0.008514404296875 -0.17425537109375 0.9510192871093776 +1.56975 -0.008758544921875 -0.17913818359375 0.9510192871093776 +1.5698750000000002 -0.00897216796875 -0.18402099609375 0.9510192871093776 +1.57 -0.00897216796875 -0.18402099609375 0.9510192871093776 +1.570125 -0.00921630859375 -0.188873291015625 0.9510192871093776 +1.57025 -0.00921630859375 -0.188873291015625 0.9510192871093776 +1.570375 -0.00946044921875 -0.1937255859375 0.9510192871093776 +1.5705 -0.00970458984375 -0.19854736328125 0.9510192871093776 +1.570625 -0.00970458984375 -0.19854736328125 0.9510192871093776 +1.57075 -0.009918212890625 -0.203338623046875 0.9510192871093776 +1.570875 -0.009918212890625 -0.203338623046875 0.9510192871093776 +1.571 -0.010162353515625 -0.208099365234375 0.9510192871093776 +1.571125 -0.010406494140625 -0.212860107421875 0.9510192871093776 +1.57125 -0.010406494140625 -0.212860107421875 0.9510192871093776 +1.571375 -0.0106201171875 -0.21759033203125 0.9510192871093776 +1.5715000000000002 -0.0106201171875 -0.21759033203125 0.9510192871093776 +1.571625 -0.0108642578125 -0.2222900390625 0.9510192871093776 +1.57175 -0.011077880859375 -0.226959228515625 0.9510192871093776 +1.5718750000000002 -0.011077880859375 -0.226959228515625 0.9510192871093776 +1.572 -0.011322021484375 -0.231597900390625 0.9510192871093776 +1.572125 -0.011322021484375 -0.231597900390625 0.9510192871093776 +1.57225 -0.01153564453125 -0.236236572265625 0.9510192871093776 +1.572375 -0.011749267578125 -0.2408447265625 0.9510192871093776 +1.5725 -0.011749267578125 -0.2408447265625 0.9510192871093776 +1.572625 -0.011993408203125 -0.24542236328125 0.9510192871093776 +1.57275 -0.011993408203125 -0.24542236328125 0.9510192871093776 +1.572875 -0.01220703125 -0.249969482421875 0.9510192871093776 +1.573 -0.012420654296875 -0.25445556640625 0.9510192871093776 +1.573125 -0.012420654296875 -0.25445556640625 0.9510192871093776 +1.57325 -0.01263427734375 -0.25897216796875 0.9510192871093776 +1.573375 -0.01263427734375 -0.25897216796875 0.9510192871093776 +1.5735000000000002 -0.01287841796875 -0.263427734375 0.9510192871093776 +1.573625 -0.013092041015625 -0.267852783203125 0.9510192871093776 +1.57375 -0.013092041015625 -0.267852783203125 0.9510192871093776 +1.5738750000000002 -0.0133056640625 -0.27227783203125 0.9510192871093776 +1.574 -0.0133056640625 -0.27227783203125 0.9510192871093776 +1.574125 -0.013519287109375 -0.276641845703125 0.9510192871093776 +1.57425 -0.01373291015625 -0.280975341796875 0.9510192871093776 +1.574375 -0.01373291015625 -0.280975341796875 0.9510192871093776 +1.5745 -0.013946533203125 -0.285308837890625 0.9510192871093776 +1.574625 -0.013946533203125 -0.285308837890625 0.9510192871093776 +1.57475 -0.014129638671875 -0.289581298828125 0.9510192871093776 +1.574875 -0.01434326171875 -0.2938232421875 0.9510192871093776 +1.575 -0.01434326171875 -0.2938232421875 0.9510192871093776 +1.575125 -0.014556884765625 -0.298065185546875 0.9510192871093776 +1.57525 -0.014556884765625 -0.298065185546875 0.9510192871093776 +1.575375 -0.0147705078125 -0.30224609375 0.9510192871093776 +1.5755000000000002 -0.01495361328125 -0.306396484375 0.9510192871093776 +1.575625 -0.01495361328125 -0.306396484375 0.9510192871093776 +1.57575 -0.015167236328125 -0.310516357421875 0.9510192871093776 +1.5758750000000002 -0.015167236328125 -0.310516357421875 0.9510192871093776 +1.576 -0.015380859375 -0.314605712890625 0.9510192871093776 +1.576125 -0.01556396484375 -0.31866455078125 0.9510192871093776 +1.57625 -0.01556396484375 -0.31866455078125 0.9510192871093776 +1.576375 -0.0157470703125 -0.322662353515625 0.9510192871093776 +1.5765 -0.0157470703125 -0.322662353515625 0.9510192871093776 +1.576625 -0.015960693359375 -0.32666015625 0.9510192871093776 +1.57675 -0.016143798828125 -0.330596923828125 0.9510192871093776 +1.576875 -0.016143798828125 -0.330596923828125 0.9510192871093776 +1.577 -0.016326904296875 -0.334503173828125 0.9510192871093776 +1.577125 -0.016326904296875 -0.334503173828125 0.9510192871093776 +1.57725 -0.01654052734375 -0.33837890625 0.9510192871093776 +1.577375 -0.0167236328125 -0.34222412109375 0.9510192871093776 +1.5775000000000002 -0.0167236328125 -0.34222412109375 0.9510192871093776 +1.577625 -0.01690673828125 -0.34600830078125 0.9510192871093776 +1.57775 -0.01690673828125 -0.34600830078125 0.9510192871093776 +1.5778750000000002 -0.01708984375 -0.349761962890625 0.9510192871093776 +1.578 -0.01727294921875 -0.353485107421875 0.9510192871093776 +1.578125 -0.01727294921875 -0.353485107421875 0.9510192871093776 +1.57825 -0.0174560546875 -0.357177734375 0.9510192871093776 +1.578375 -0.0174560546875 -0.357177734375 0.9510192871093776 +1.5785 -0.01763916015625 -0.360809326171875 0.9510192871093776 +1.578625 -0.017791748046875 -0.36444091796875 0.9510192871093776 +1.57875 -0.017791748046875 -0.36444091796875 0.9510192871093776 +1.578875 -0.017974853515625 -0.36798095703125 0.9510192871093776 +1.579 -0.017974853515625 -0.36798095703125 0.9510192871093776 +1.5791250000000002 -0.018157958984375 -0.37152099609375 0.9510192871093776 +1.57925 -0.018310546875 -0.375 0.9510192871093776 +1.579375 -0.018310546875 -0.375 0.9510192871093776 +1.5795000000000002 -0.01849365234375 -0.378448486328125 0.9510192871093776 +1.579625 -0.01849365234375 -0.378448486328125 0.9510192871093776 +1.57975 -0.018646240234375 -0.3818359375 0.9510192871093776 +1.5798750000000002 -0.018829345703125 -0.38519287109375 0.9510192871093776 +1.58 -0.018829345703125 -0.38519287109375 0.9510192871093776 +1.580125 -0.01898193359375 -0.388519287109375 0.9510192871093776 +1.58025 -0.01898193359375 -0.388519287109375 0.9510192871093776 +1.580375 -0.019134521484375 -0.39178466796875 0.9510192871093776 +1.5805 -0.019287109375 -0.39501953125 0.9510192871093776 +1.580625 -0.019287109375 -0.39501953125 0.9510192871093776 +1.58075 -0.019439697265625 -0.398193359375 0.9510192871093776 +1.580875 -0.019439697265625 -0.398193359375 0.9510192871093776 +1.581 -0.019622802734375 -0.401336669921875 0.9510192871093776 +1.5811250000000002 -0.019775390625 -0.404449462890625 0.9510192871093776 +1.58125 -0.019775390625 -0.404449462890625 0.9510192871093776 +1.581375 -0.0198974609375 -0.407501220703125 0.9510192871093776 +1.5815000000000002 -0.0198974609375 -0.407501220703125 0.9510192871093776 +1.581625 -0.020050048828125 -0.4105224609375 0.9510192871093776 +1.58175 -0.02020263671875 -0.413482666015625 0.9510192871093776 +1.5818750000000002 -0.02020263671875 -0.413482666015625 0.9510192871093776 +1.582 -0.020355224609375 -0.416412353515625 0.9510192871093776 +1.582125 -0.020355224609375 -0.416412353515625 0.9510192871093776 +1.58225 -0.020477294921875 -0.419281005859375 0.9510192871093776 +1.582375 -0.0206298828125 -0.422119140625 0.9510192871093776 +1.5825 -0.0206298828125 -0.422119140625 0.9510192871093776 +1.582625 -0.020751953125 -0.424896240234375 0.9510192871093776 +1.58275 -0.020751953125 -0.424896240234375 0.9510192871093776 +1.582875 -0.020904541015625 -0.4276123046875 0.9510192871093776 +1.583 -0.021026611328125 -0.4302978515625 0.9510192871093776 +1.5831250000000002 -0.021026611328125 -0.4302978515625 0.9510192871093776 +1.58325 -0.021148681640625 -0.432952880859375 0.9510192871093776 +1.583375 -0.021148681640625 -0.432952880859375 0.9510192871093776 +1.5835000000000002 -0.021270751953125 -0.435546875 0.9510192871093776 +1.583625 -0.021392822265625 -0.438079833984375 0.9510192871093776 +1.58375 -0.021392822265625 -0.438079833984375 0.9510192871093776 +1.5838750000000002 -0.021514892578125 -0.440582275390625 0.9510192871093776 +1.584 -0.021514892578125 -0.440582275390625 0.9510192871093776 +1.584125 -0.021636962890625 -0.44305419921875 0.9510192871093776 +1.58425 -0.021759033203125 -0.4454345703125 0.9510192871093776 +1.584375 -0.021759033203125 -0.4454345703125 0.9510192871093776 +1.5845 -0.021881103515625 -0.447784423828125 0.9510192871093776 +1.584625 -0.021881103515625 -0.447784423828125 0.9510192871093776 +1.58475 -0.022003173828125 -0.450103759765625 0.9510192871093776 +1.584875 -0.0220947265625 -0.452362060546875 0.9510192871093776 +1.585 -0.0220947265625 -0.452362060546875 0.9510192871093776 +1.5851250000000002 -0.022216796875 -0.454559326171875 0.9510192871093776 +1.58525 -0.022216796875 -0.454559326171875 0.9510192871093776 +1.585375 -0.022308349609375 -0.45672607421875 0.9510192871093776 +1.5855000000000002 -0.022430419921875 -0.458831787109375 0.9510192871093776 +1.585625 -0.022430419921875 -0.458831787109375 0.9510192871093776 +1.58575 -0.02252197265625 -0.46087646484375 0.9510192871093776 +1.585875 -0.02252197265625 -0.46087646484375 0.9510192871093776 +1.586 -0.022613525390625 -0.462890625 0.9510192871093776 +1.586125 -0.022705078125 -0.464813232421875 0.9510192871093776 +1.58625 -0.022705078125 -0.464813232421875 0.9510192871093776 +1.586375 -0.022796630859375 -0.46673583984375 0.9510192871093776 +1.5865 -0.022796630859375 -0.46673583984375 0.9510192871093776 +1.586625 -0.02288818359375 -0.46856689453125 0.9510192871093776 +1.58675 -0.022979736328125 -0.470367431640625 0.9510192871093776 +1.586875 -0.022979736328125 -0.470367431640625 0.9510192871093776 +1.587 -0.0230712890625 -0.472137451171875 0.9510192871093776 +1.5871250000000002 -0.0230712890625 -0.472137451171875 0.9510192871093776 +1.58725 -0.023162841796875 -0.47381591796875 0.9510192871093776 +1.587375 -0.023223876953125 -0.4754638671875 0.9510192871093776 +1.5875000000000002 -0.023223876953125 -0.4754638671875 0.9510192871093776 +1.587625 -0.0233154296875 -0.47705078125 0.9510192871093776 +1.58775 -0.0233154296875 -0.47705078125 0.9510192871093776 +1.587875 -0.02337646484375 -0.478607177734375 0.9510192871093776 +1.588 -0.023468017578125 -0.480072021484375 0.9510192871093776 +1.588125 -0.023468017578125 -0.480072021484375 0.9510192871093776 +1.58825 -0.023529052734375 -0.48150634765625 0.9510192871093776 +1.588375 -0.023529052734375 -0.48150634765625 0.9510192871093776 +1.5885 -0.023590087890625 -0.48291015625 0.9510192871093776 +1.588625 -0.023651123046875 -0.484222412109375 0.9510192871093776 +1.58875 -0.023651123046875 -0.484222412109375 0.9510192871093776 +1.588875 -0.023712158203125 -0.485504150390625 0.9510192871093776 +1.589 -0.023712158203125 -0.485504150390625 0.9510192871093776 +1.5891250000000002 -0.023773193359375 -0.486724853515625 0.9510192871093776 +1.58925 -0.023834228515625 -0.487884521484375 0.9510192871093776 +1.589375 -0.023834228515625 -0.487884521484375 0.9510192871093776 +1.5895000000000002 -0.023895263671875 -0.489013671875 0.9510192871093776 +1.589625 -0.023895263671875 -0.489013671875 0.9510192871093776 +1.58975 -0.023956298828125 -0.490081787109375 0.9510192871093776 +1.589875 -0.02398681640625 -0.4910888671875 0.9510192871093776 +1.59 -0.02398681640625 -0.4910888671875 0.9510192871093776 +1.590125 -0.0240478515625 -0.492034912109375 0.9510192871093776 +1.59025 -0.0240478515625 -0.492034912109375 0.9510192871093776 +1.590375 -0.024078369140625 -0.492950439453125 0.9510192871093776 +1.5905 -0.024139404296875 -0.4937744140625 0.9510192871093776 +1.590625 -0.024139404296875 -0.4937744140625 0.9510192871093776 +1.59075 -0.024169921875 -0.49456787109375 0.9510192871093776 +1.590875 -0.024169921875 -0.49456787109375 0.9510192871093776 +1.591 -0.024200439453125 -0.49530029296875 0.9510192871093776 +1.5911250000000002 -0.02423095703125 -0.496002197265625 0.9510192871093776 +1.59125 -0.02423095703125 -0.496002197265625 0.9510192871093776 +1.591375 -0.024261474609375 -0.496612548828125 0.9510192871093776 +1.5915000000000002 -0.024261474609375 -0.496612548828125 0.9510192871093776 +1.591625 -0.0242919921875 -0.4971923828125 0.9510192871093776 +1.59175 -0.024322509765625 -0.497711181640625 0.9510192871093776 +1.591875 -0.024322509765625 -0.497711181640625 0.9510192871093776 +1.592 -0.02435302734375 -0.498199462890625 0.9510192871093776 +1.592125 -0.02435302734375 -0.498199462890625 0.9510192871093776 +1.59225 -0.02435302734375 -0.49859619140625 0.9510192871093776 +1.592375 -0.024383544921875 -0.49896240234375 0.9510192871093776 +1.5925 -0.024383544921875 -0.49896240234375 0.9510192871093776 +1.592625 -0.024383544921875 -0.499267578125 0.9510192871093776 +1.59275 -0.024383544921875 -0.499267578125 0.9510192871093776 +1.592875 -0.0244140625 -0.49951171875 0.9510192871093776 +1.593 -0.0244140625 -0.49969482421875 0.9510192871093776 +1.5931250000000002 -0.0244140625 -0.49969482421875 0.9510192871093776 +1.59325 -0.0244140625 -0.49981689453125 0.9510192871093776 +1.593375 -0.0244140625 -0.49981689453125 0.9510192871093776 +1.5935000000000002 -0.0244140625 -0.499908447265625 0.9510192871093776 +1.593625 -0.0244140625 -0.49993896484375 0.9510192871093776 +1.59375 -0.0244140625 -0.49993896484375 0.9510192871093776 +1.593875 -0.0244140625 -0.499908447265625 0.9510192871093776 +1.594 -0.0244140625 -0.499908447265625 0.9510192871093776 +1.594125 -0.0244140625 -0.49981689453125 0.9510192871093776 +1.59425 -0.0244140625 -0.49969482421875 0.9510192871093776 +1.594375 -0.0244140625 -0.49969482421875 0.9510192871093776 +1.5945 -0.0244140625 -0.49951171875 0.9510192871093776 +1.594625 -0.0244140625 -0.49951171875 0.9510192871093776 +1.5947500000000002 -0.024383544921875 -0.499267578125 0.9510192871093776 +1.594875 -0.024383544921875 -0.49896240234375 0.9510192871093776 +1.595 -0.024383544921875 -0.49896240234375 0.9510192871093776 +1.5951250000000002 -0.02435302734375 -0.49859619140625 0.9510192871093776 +1.59525 -0.02435302734375 -0.49859619140625 0.9510192871093776 +1.595375 -0.02435302734375 -0.498199462890625 0.9510192871093776 +1.5955000000000002 -0.024322509765625 -0.497711181640625 0.9510192871093776 +1.595625 -0.024322509765625 -0.497711181640625 0.9510192871093776 +1.59575 -0.0242919921875 -0.4971923828125 0.9510192871093776 +1.595875 -0.0242919921875 -0.4971923828125 0.9510192871093776 +1.596 -0.024261474609375 -0.496612548828125 0.9510192871093776 +1.596125 -0.02423095703125 -0.496002197265625 0.9510192871093776 +1.59625 -0.02423095703125 -0.496002197265625 0.9510192871093776 +1.596375 -0.024200439453125 -0.49530029296875 0.9510192871093776 +1.5965 -0.024200439453125 -0.49530029296875 0.9510192871093776 +1.596625 -0.024169921875 -0.49456787109375 0.9510192871093776 +1.5967500000000002 -0.024139404296875 -0.4937744140625 0.9510192871093776 +1.596875 -0.024139404296875 -0.4937744140625 0.9510192871093776 +1.597 -0.024078369140625 -0.492950439453125 0.9510192871093776 +1.5971250000000002 -0.024078369140625 -0.492950439453125 0.9510192871093776 +1.59725 -0.0240478515625 -0.492034912109375 0.9510192871093776 +1.597375 -0.02398681640625 -0.4910888671875 0.9510192871093776 +1.5975000000000002 -0.02398681640625 -0.4910888671875 0.9510192871093776 +1.597625 -0.023956298828125 -0.490081787109375 0.9510192871093776 +1.59775 -0.023956298828125 -0.490081787109375 0.9510192871093776 +1.597875 -0.023895263671875 -0.489013671875 0.9510192871093776 +1.598 -0.023834228515625 -0.487884521484375 0.9510192871093776 +1.598125 -0.023834228515625 -0.487884521484375 0.9510192871093776 +1.59825 -0.023773193359375 -0.486724853515625 0.9510192871093776 +1.598375 -0.023773193359375 -0.486724853515625 0.9510192871093776 +1.5985 -0.023712158203125 -0.485504150390625 0.9510192871093776 +1.598625 -0.023651123046875 -0.484222412109375 0.9510192871093776 +1.5987500000000002 -0.023651123046875 -0.484222412109375 0.9510192871093776 +1.598875 -0.023590087890625 -0.48291015625 0.9510192871093776 +1.599 -0.023590087890625 -0.48291015625 0.9510192871093776 +1.5991250000000002 -0.023529052734375 -0.48150634765625 0.9510192871093776 +1.59925 -0.023468017578125 -0.480072021484375 0.9510192871093776 +1.599375 -0.023468017578125 -0.480072021484375 0.9510192871093776 +1.5995000000000002 -0.02337646484375 -0.478607177734375 0.9510192871093776 +1.599625 -0.02337646484375 -0.478607177734375 0.9510192871093776 +1.59975 -0.0233154296875 -0.47705078125 0.9510192871093776 +1.599875 -0.023223876953125 -0.4754638671875 0.9510192871093776 +1.6 -0.001800537109375 -0.4754638671875 0.99608154296874928 +1.600125 -0.001800537109375 -0.47381591796875 0.99608154296874928 +1.60025 -0.001800537109375 -0.47381591796875 0.99608154296874928 +1.600375 -0.001800537109375 -0.472137451171875 0.99608154296874928 +1.6005 -0.00177001953125 -0.470367431640625 0.99608154296874928 +1.600625 -0.00177001953125 -0.470367431640625 0.99608154296874928 +1.6007500000000002 -0.00177001953125 -0.46856689453125 0.99608154296874928 +1.600875 -0.00177001953125 -0.46856689453125 0.99608154296874928 +1.601 -0.00177001953125 -0.46673583984375 0.99608154296874928 +1.6011250000000002 -0.00177001953125 -0.464813232421875 0.99608154296874928 +1.60125 -0.00177001953125 -0.464813232421875 0.99608154296874928 +1.601375 -0.001739501953125 -0.462890625 0.99608154296874928 +1.6015 -0.001739501953125 -0.462890625 0.99608154296874928 +1.601625 -0.001739501953125 -0.46087646484375 0.99608154296874928 +1.60175 -0.001739501953125 -0.458831787109375 0.99608154296874928 +1.601875 -0.001739501953125 -0.458831787109375 0.99608154296874928 +1.602 -0.001739501953125 -0.45672607421875 0.99608154296874928 +1.602125 -0.001739501953125 -0.45672607421875 0.99608154296874928 +1.60225 -0.001708984375 -0.454559326171875 0.99608154296874928 +1.602375 -0.001708984375 -0.452362060546875 0.99608154296874928 +1.6025 -0.001708984375 -0.452362060546875 0.99608154296874928 +1.602625 -0.001708984375 -0.450103759765625 0.99608154296874928 +1.6027500000000002 -0.001708984375 -0.450103759765625 0.99608154296874928 +1.602875 -0.001678466796875 -0.447784423828125 0.99608154296874928 +1.603 -0.001678466796875 -0.4454345703125 0.99608154296874928 +1.6031250000000002 -0.001678466796875 -0.4454345703125 0.99608154296874928 +1.60325 -0.001678466796875 -0.44305419921875 0.99608154296874928 +1.603375 -0.001678466796875 -0.44305419921875 0.99608154296874928 +1.6035 -0.001678466796875 -0.440582275390625 0.99608154296874928 +1.603625 -0.00164794921875 -0.438079833984375 0.99608154296874928 +1.60375 -0.00164794921875 -0.438079833984375 0.99608154296874928 +1.603875 -0.00164794921875 -0.435546875 0.99608154296874928 +1.604 -0.00164794921875 -0.435546875 0.99608154296874928 +1.604125 -0.00164794921875 -0.432952880859375 0.99608154296874928 +1.60425 -0.001617431640625 -0.4302978515625 0.99608154296874928 +1.604375 -0.001617431640625 -0.4302978515625 0.99608154296874928 +1.6045 -0.001617431640625 -0.4276123046875 0.99608154296874928 +1.604625 -0.001617431640625 -0.4276123046875 0.99608154296874928 +1.6047500000000002 -0.001617431640625 -0.424896240234375 0.99608154296874928 +1.604875 -0.0015869140625 -0.422119140625 0.99608154296874928 +1.605 -0.0015869140625 -0.422119140625 0.99608154296874928 +1.6051250000000002 -0.0015869140625 -0.419281005859375 0.99608154296874928 +1.60525 -0.0015869140625 -0.419281005859375 0.99608154296874928 +1.605375 -0.0015869140625 -0.416412353515625 0.99608154296874928 +1.6055 -0.001556396484375 -0.413482666015625 0.99608154296874928 +1.605625 -0.001556396484375 -0.413482666015625 0.99608154296874928 +1.60575 -0.001556396484375 -0.4105224609375 0.99608154296874928 +1.605875 -0.001556396484375 -0.4105224609375 0.99608154296874928 +1.606 -0.00152587890625 -0.407501220703125 0.99608154296874928 +1.606125 -0.00152587890625 -0.404449462890625 0.99608154296874928 +1.60625 -0.00152587890625 -0.404449462890625 0.99608154296874928 +1.606375 -0.00152587890625 -0.401336669921875 0.99608154296874928 +1.6065 -0.00152587890625 -0.401336669921875 0.99608154296874928 +1.606625 -0.001495361328125 -0.398193359375 0.99608154296874928 +1.6067500000000002 -0.001495361328125 -0.39501953125 0.99608154296874928 +1.606875 -0.001495361328125 -0.39501953125 0.99608154296874928 +1.607 -0.00146484375 -0.39178466796875 0.99608154296874928 +1.6071250000000002 -0.00146484375 -0.39178466796875 0.99608154296874928 +1.60725 -0.00146484375 -0.388519287109375 0.99608154296874928 +1.607375 -0.00146484375 -0.38519287109375 0.99608154296874928 +1.6075 -0.00146484375 -0.38519287109375 0.99608154296874928 +1.607625 -0.001434326171875 -0.3818359375 0.99608154296874928 +1.60775 -0.001434326171875 -0.3818359375 0.99608154296874928 +1.607875 -0.001434326171875 -0.378448486328125 0.99608154296874928 +1.608 -0.00140380859375 -0.375 0.99608154296874928 +1.608125 -0.00140380859375 -0.375 0.99608154296874928 +1.60825 -0.00140380859375 -0.37152099609375 0.99608154296874928 +1.608375 -0.00140380859375 -0.37152099609375 0.99608154296874928 +1.6085 -0.00140380859375 -0.36798095703125 0.99608154296874928 +1.608625 -0.001373291015625 -0.36444091796875 0.99608154296874928 +1.6087500000000002 -0.001373291015625 -0.36444091796875 0.99608154296874928 +1.608875 -0.001373291015625 -0.360809326171875 0.99608154296874928 +1.609 -0.001373291015625 -0.360809326171875 0.99608154296874928 +1.6091250000000002 -0.0013427734375 -0.357177734375 0.99608154296874928 +1.60925 -0.0013427734375 -0.353485107421875 0.99608154296874928 +1.609375 -0.0013427734375 -0.353485107421875 0.99608154296874928 +1.6095 -0.001312255859375 -0.349761962890625 0.99608154296874928 +1.609625 -0.001312255859375 -0.349761962890625 0.99608154296874928 +1.60975 -0.001312255859375 -0.34600830078125 0.99608154296874928 +1.609875 -0.00128173828125 -0.34222412109375 0.99608154296874928 +1.61 -0.00128173828125 -0.34222412109375 0.99608154296874928 +1.610125 -0.00128173828125 -0.33837890625 0.99608154296874928 +1.61025 -0.00128173828125 -0.33837890625 0.99608154296874928 +1.6103750000000002 -0.001251220703125 -0.334503173828125 0.99608154296874928 +1.6105 -0.001251220703125 -0.330596923828125 0.99608154296874928 +1.610625 -0.001251220703125 -0.330596923828125 0.99608154296874928 +1.6107500000000002 -0.001220703125 -0.32666015625 0.99608154296874928 +1.610875 -0.001220703125 -0.32666015625 0.99608154296874928 +1.611 -0.001220703125 -0.322662353515625 0.99608154296874928 +1.6111250000000002 -0.001190185546875 -0.31866455078125 0.99608154296874928 +1.61125 -0.001190185546875 -0.31866455078125 0.99608154296874928 +1.611375 -0.001190185546875 -0.314605712890625 0.99608154296874928 +1.6115 -0.001190185546875 -0.314605712890625 0.99608154296874928 +1.611625 -0.00115966796875 -0.310516357421875 0.99608154296874928 +1.61175 -0.00115966796875 -0.306396484375 0.99608154296874928 +1.611875 -0.00115966796875 -0.306396484375 0.99608154296874928 +1.612 -0.001129150390625 -0.30224609375 0.99608154296874928 +1.612125 -0.001129150390625 -0.30224609375 0.99608154296874928 +1.61225 -0.001129150390625 -0.298065185546875 0.99608154296874928 +1.6123750000000002 -0.0010986328125 -0.2938232421875 0.99608154296874928 +1.6125 -0.0010986328125 -0.2938232421875 0.99608154296874928 +1.612625 -0.0010986328125 -0.289581298828125 0.99608154296874928 +1.6127500000000002 -0.0010986328125 -0.289581298828125 0.99608154296874928 +1.612875 -0.001068115234375 -0.285308837890625 0.99608154296874928 +1.613 -0.001068115234375 -0.280975341796875 0.99608154296874928 +1.6131250000000002 -0.001068115234375 -0.280975341796875 0.99608154296874928 +1.61325 -0.00103759765625 -0.276641845703125 0.99608154296874928 +1.613375 -0.00103759765625 -0.276641845703125 0.99608154296874928 +1.6135 -0.00103759765625 -0.27227783203125 0.99608154296874928 +1.613625 -0.001007080078125 -0.267852783203125 0.99608154296874928 +1.61375 -0.001007080078125 -0.267852783203125 0.99608154296874928 +1.613875 -0.0009765625 -0.263427734375 0.99608154296874928 +1.614 -0.0009765625 -0.263427734375 0.99608154296874928 +1.614125 -0.0009765625 -0.25897216796875 0.99608154296874928 +1.61425 -0.000946044921875 -0.25445556640625 0.99608154296874928 +1.6143750000000002 -0.000946044921875 -0.25445556640625 0.99608154296874928 +1.6145 -0.000946044921875 -0.249969482421875 0.99608154296874928 +1.614625 -0.000946044921875 -0.249969482421875 0.99608154296874928 +1.6147500000000002 -0.00091552734375 -0.24542236328125 0.99608154296874928 +1.614875 -0.00091552734375 -0.2408447265625 0.99608154296874928 +1.615 -0.00091552734375 -0.2408447265625 0.99608154296874928 +1.6151250000000002 -0.000885009765625 -0.236236572265625 0.99608154296874928 +1.61525 -0.000885009765625 -0.236236572265625 0.99608154296874928 +1.615375 -0.0008544921875 -0.231597900390625 0.99608154296874928 +1.6155 -0.0008544921875 -0.226959228515625 0.99608154296874928 +1.615625 -0.0008544921875 -0.226959228515625 0.99608154296874928 +1.61575 -0.000823974609375 -0.2222900390625 0.99608154296874928 +1.615875 -0.000823974609375 -0.2222900390625 0.99608154296874928 +1.616 -0.000823974609375 -0.21759033203125 0.99608154296874928 +1.616125 -0.00079345703125 -0.212860107421875 0.99608154296874928 +1.61625 -0.00079345703125 -0.212860107421875 0.99608154296874928 +1.6163750000000002 -0.00079345703125 -0.208099365234375 0.99608154296874928 +1.6165 -0.00079345703125 -0.208099365234375 0.99608154296874928 +1.616625 -0.000762939453125 -0.203338623046875 0.99608154296874928 +1.6167500000000002 -0.000732421875 -0.19854736328125 0.99608154296874928 +1.616875 -0.000732421875 -0.19854736328125 0.99608154296874928 +1.617 -0.000732421875 -0.1937255859375 0.99608154296874928 +1.617125 -0.000732421875 -0.1937255859375 0.99608154296874928 +1.61725 -0.000701904296875 -0.188873291015625 0.99608154296874928 +1.617375 -0.000701904296875 -0.18402099609375 0.99608154296874928 +1.6175 -0.000701904296875 -0.18402099609375 0.99608154296874928 +1.617625 -0.00067138671875 -0.17913818359375 0.99608154296874928 +1.61775 -0.00067138671875 -0.17913818359375 0.99608154296874928 +1.617875 -0.000640869140625 -0.17425537109375 0.99608154296874928 +1.618 -0.000640869140625 -0.169342041015625 0.99608154296874928 +1.618125 -0.000640869140625 -0.169342041015625 0.99608154296874928 +1.61825 -0.0006103515625 -0.164398193359375 0.99608154296874928 +1.6183750000000002 -0.0006103515625 -0.164398193359375 0.99608154296874928 +1.6185 -0.000579833984375 -0.159454345703125 0.99608154296874928 +1.618625 -0.000579833984375 -0.15447998046875 0.99608154296874928 +1.6187500000000002 -0.000579833984375 -0.15447998046875 0.99608154296874928 +1.618875 -0.00054931640625 -0.14947509765625 0.99608154296874928 +1.619 -0.00054931640625 -0.14947509765625 0.99608154296874928 +1.619125 -0.00054931640625 -0.14447021484375 0.99608154296874928 +1.61925 -0.000518798828125 -0.13946533203125 0.99608154296874928 +1.619375 -0.000518798828125 -0.13946533203125 0.99608154296874928 +1.6195 -0.00048828125 -0.134429931640625 0.99608154296874928 +1.619625 -0.00048828125 -0.134429931640625 0.99608154296874928 +1.61975 -0.00048828125 -0.129364013671875 0.99608154296874928 +1.619875 -0.000457763671875 -0.124298095703125 0.99608154296874928 +1.62 -0.000457763671875 -0.124298095703125 0.99608154296874928 +1.620125 -0.00042724609375 -0.119232177734375 0.99608154296874928 +1.62025 -0.00042724609375 -0.119232177734375 0.99608154296874928 +1.6203750000000002 -0.00042724609375 -0.1141357421875 0.99608154296874928 +1.6205 -0.000396728515625 -0.109039306640625 0.99608154296874928 +1.620625 -0.000396728515625 -0.109039306640625 0.99608154296874928 +1.6207500000000002 -0.0003662109375 -0.103912353515625 0.99608154296874928 +1.620875 -0.0003662109375 -0.103912353515625 0.99608154296874928 +1.621 -0.0003662109375 -0.098785400390625 0.99608154296874928 +1.621125 -0.000335693359375 -0.093658447265625 0.99608154296874928 +1.62125 -0.000335693359375 -0.093658447265625 0.99608154296874928 +1.621375 -0.000335693359375 -0.0885009765625 0.99608154296874928 +1.6215 -0.000335693359375 -0.0885009765625 0.99608154296874928 +1.621625 -0.00030517578125 -0.083343505859375 0.99608154296874928 +1.62175 -0.000274658203125 -0.07818603515625 0.99608154296874928 +1.621875 -0.000274658203125 -0.07818603515625 0.99608154296874928 +1.622 -0.000274658203125 -0.072998046875 0.99608154296874928 +1.622125 -0.000274658203125 -0.072998046875 0.99608154296874928 +1.62225 -0.000244140625 -0.06781005859375 0.99608154296874928 +1.6223750000000002 -0.000213623046875 -0.0626220703125 0.99608154296874928 +1.6225 -0.000213623046875 -0.0626220703125 0.99608154296874928 +1.622625 -0.000213623046875 -0.05743408203125 0.99608154296874928 +1.6227500000000002 -0.000213623046875 -0.05743408203125 0.99608154296874928 +1.622875 -0.00018310546875 -0.05224609375 0.99608154296874928 +1.623 -0.000152587890625 -0.047027587890625 0.99608154296874928 +1.623125 -0.000152587890625 -0.047027587890625 0.99608154296874928 +1.62325 -0.000152587890625 -0.04180908203125 0.99608154296874928 +1.623375 -0.000152587890625 -0.04180908203125 0.99608154296874928 +1.6235 -0.0001220703125 -0.036590576171875 0.99608154296874928 +1.623625 -9.1552734375e-05 -0.0313720703125 0.99608154296874928 +1.62375 -9.1552734375e-05 -0.0313720703125 0.99608154296874928 +1.623875 -9.1552734375e-05 -0.026123046875 0.99608154296874928 +1.624 -9.1552734375e-05 -0.026123046875 0.99608154296874928 +1.624125 -6.103515625e-05 -0.020904541015625 0.99608154296874928 +1.62425 -3.0517578125e-05 -0.01568603515625 0.99608154296874928 +1.6243750000000002 -3.0517578125e-05 -0.01568603515625 0.99608154296874928 +1.6245 -3.0517578125e-05 -0.01043701171875 0.99608154296874928 +1.624625 -3.0517578125e-05 -0.01043701171875 0.99608154296874928 +1.6247500000000002 0.0 -0.005218505859375 0.99608154296874928 +1.624875 0.0 0.0 0.99608154296874928 +1.625 0.0 0.0 0.99608154296874928 +1.625125 0.0 0.005218505859375 0.99608154296874928 +1.62525 0.0 0.005218505859375 0.99608154296874928 +1.625375 3.0517578125e-05 0.01043701171875 0.99608154296874928 +1.6255 3.0517578125e-05 0.01568603515625 0.99608154296874928 +1.625625 3.0517578125e-05 0.01568603515625 0.99608154296874928 +1.62575 6.103515625e-05 0.020904541015625 0.99608154296874928 +1.625875 6.103515625e-05 0.020904541015625 0.99608154296874928 +1.6260000000000002 9.1552734375e-05 0.026123046875 0.99608154296874928 +1.626125 9.1552734375e-05 0.0313720703125 0.99608154296874928 +1.62625 9.1552734375e-05 0.0313720703125 0.99608154296874928 +1.6263750000000002 0.0001220703125 0.036590576171875 0.99608154296874928 +1.6265 0.0001220703125 0.036590576171875 0.99608154296874928 +1.626625 0.000152587890625 0.04180908203125 0.99608154296874928 +1.6267500000000002 0.000152587890625 0.047027587890625 0.99608154296874928 +1.626875 0.000152587890625 0.047027587890625 0.99608154296874928 +1.627 0.00018310546875 0.05224609375 0.99608154296874928 +1.627125 0.00018310546875 0.05224609375 0.99608154296874928 +1.62725 0.000213623046875 0.05743408203125 0.99608154296874928 +1.627375 0.000213623046875 0.0626220703125 0.99608154296874928 +1.6275 0.000213623046875 0.0626220703125 0.99608154296874928 +1.627625 0.000244140625 0.06781005859375 0.99608154296874928 +1.62775 0.000244140625 0.06781005859375 0.99608154296874928 +1.627875 0.000274658203125 0.072998046875 0.99608154296874928 +1.6280000000000002 0.000274658203125 0.07818603515625 0.99608154296874928 +1.628125 0.000274658203125 0.07818603515625 0.99608154296874928 +1.62825 0.00030517578125 0.083343505859375 0.99608154296874928 +1.6283750000000002 0.00030517578125 0.083343505859375 0.99608154296874928 +1.6285 0.000335693359375 0.0885009765625 0.99608154296874928 +1.628625 0.000335693359375 0.093658447265625 0.99608154296874928 +1.6287500000000002 0.000335693359375 0.093658447265625 0.99608154296874928 +1.628875 0.0003662109375 0.098785400390625 0.99608154296874928 +1.629 0.0003662109375 0.098785400390625 0.99608154296874928 +1.629125 0.0003662109375 0.103912353515625 0.99608154296874928 +1.62925 0.000396728515625 0.109039306640625 0.99608154296874928 +1.629375 0.000396728515625 0.109039306640625 0.99608154296874928 +1.6295 0.00042724609375 0.1141357421875 0.99608154296874928 +1.629625 0.00042724609375 0.1141357421875 0.99608154296874928 +1.62975 0.00042724609375 0.119232177734375 0.99608154296874928 +1.629875 0.000457763671875 0.124298095703125 0.99608154296874928 +1.6300000000000002 0.000457763671875 0.124298095703125 0.99608154296874928 +1.630125 0.00048828125 0.129364013671875 0.99608154296874928 +1.63025 0.00048828125 0.129364013671875 0.99608154296874928 +1.6303750000000002 0.00048828125 0.134429931640625 0.99608154296874928 +1.6305 0.000518798828125 0.13946533203125 0.99608154296874928 +1.630625 0.000518798828125 0.13946533203125 0.99608154296874928 +1.6307500000000002 0.00054931640625 0.14447021484375 0.99608154296874928 +1.630875 0.00054931640625 0.14447021484375 0.99608154296874928 +1.631 0.00054931640625 0.14947509765625 0.99608154296874928 +1.631125 0.000579833984375 0.15447998046875 0.99608154296874928 +1.63125 0.000579833984375 0.15447998046875 0.99608154296874928 +1.631375 0.000579833984375 0.159454345703125 0.99608154296874928 +1.6315 0.000579833984375 0.159454345703125 0.99608154296874928 +1.631625 0.0006103515625 0.164398193359375 0.99608154296874928 +1.63175 0.000640869140625 0.169342041015625 0.99608154296874928 +1.631875 0.000640869140625 0.169342041015625 0.99608154296874928 +1.6320000000000002 0.020477294921875 0.17425537109375 0.882238769531246 +1.632125 0.020477294921875 0.17425537109375 0.882238769531246 +1.63225 0.02105712890625 0.17913818359375 0.882238769531246 +1.6323750000000002 0.021636962890625 0.18402099609375 0.882238769531246 +1.6325 0.021636962890625 0.18402099609375 0.882238769531246 +1.632625 0.022216796875 0.188873291015625 0.882238769531246 +1.63275 0.022216796875 0.188873291015625 0.882238769531246 +1.632875 0.022796630859375 0.1937255859375 0.882238769531246 +1.633 0.023345947265625 0.19854736328125 0.882238769531246 +1.633125 0.023345947265625 0.19854736328125 0.882238769531246 +1.63325 0.02392578125 0.203338623046875 0.882238769531246 +1.633375 0.02392578125 0.203338623046875 0.882238769531246 +1.6335 0.02447509765625 0.208099365234375 0.882238769531246 +1.633625 0.0250244140625 0.212860107421875 0.882238769531246 +1.63375 0.0250244140625 0.212860107421875 0.882238769531246 +1.633875 0.025604248046875 0.21759033203125 0.882238769531246 +1.6340000000000002 0.025604248046875 0.21759033203125 0.882238769531246 +1.634125 0.026153564453125 0.2222900390625 0.882238769531246 +1.63425 0.026702880859375 0.226959228515625 0.882238769531246 +1.6343750000000002 0.026702880859375 0.226959228515625 0.882238769531246 +1.6345 0.027252197265625 0.231597900390625 0.882238769531246 +1.634625 0.027252197265625 0.231597900390625 0.882238769531246 +1.63475 0.027801513671875 0.236236572265625 0.882238769531246 +1.634875 0.0283203125 0.2408447265625 0.882238769531246 +1.635 0.0283203125 0.2408447265625 0.882238769531246 +1.635125 0.02886962890625 0.24542236328125 0.882238769531246 +1.63525 0.02886962890625 0.24542236328125 0.882238769531246 +1.635375 0.029388427734375 0.249969482421875 0.882238769531246 +1.6355 0.029937744140625 0.25445556640625 0.882238769531246 +1.635625 0.029937744140625 0.25445556640625 0.882238769531246 +1.63575 0.03045654296875 0.25897216796875 0.882238769531246 +1.635875 0.03045654296875 0.25897216796875 0.882238769531246 +1.6360000000000002 0.030975341796875 0.263427734375 0.882238769531246 +1.636125 0.031524658203125 0.267852783203125 0.882238769531246 +1.63625 0.031524658203125 0.267852783203125 0.882238769531246 +1.6363750000000002 0.03204345703125 0.27227783203125 0.882238769531246 +1.6365 0.03204345703125 0.27227783203125 0.882238769531246 +1.636625 0.03253173828125 0.276641845703125 0.882238769531246 +1.63675 0.033050537109375 0.280975341796875 0.882238769531246 +1.636875 0.033050537109375 0.280975341796875 0.882238769531246 +1.637 0.0335693359375 0.285308837890625 0.882238769531246 +1.637125 0.0335693359375 0.285308837890625 0.882238769531246 +1.63725 0.0340576171875 0.289581298828125 0.882238769531246 +1.637375 0.034576416015625 0.2938232421875 0.882238769531246 +1.6375 0.034576416015625 0.2938232421875 0.882238769531246 +1.637625 0.035064697265625 0.298065185546875 0.882238769531246 +1.63775 0.035064697265625 0.298065185546875 0.882238769531246 +1.637875 0.035552978515625 0.30224609375 0.882238769531246 +1.6380000000000002 0.036041259765625 0.306396484375 0.882238769531246 +1.638125 0.036041259765625 0.306396484375 0.882238769531246 +1.63825 0.036529541015625 0.310516357421875 0.882238769531246 +1.6383750000000002 0.036529541015625 0.310516357421875 0.882238769531246 +1.6385 0.037017822265625 0.314605712890625 0.882238769531246 +1.638625 0.0374755859375 0.31866455078125 0.882238769531246 +1.63875 0.0374755859375 0.31866455078125 0.882238769531246 +1.638875 0.0379638671875 0.322662353515625 0.882238769531246 +1.639 0.0379638671875 0.322662353515625 0.882238769531246 +1.639125 0.038421630859375 0.32666015625 0.882238769531246 +1.63925 0.03887939453125 0.330596923828125 0.882238769531246 +1.639375 0.03887939453125 0.330596923828125 0.882238769531246 +1.6395 0.03936767578125 0.334503173828125 0.882238769531246 +1.639625 0.03936767578125 0.334503173828125 0.882238769531246 +1.63975 0.039794921875 0.33837890625 0.882238769531246 +1.639875 0.040252685546875 0.34222412109375 0.882238769531246 +1.6400000000000002 0.040252685546875 0.34222412109375 0.882238769531246 +1.640125 0.04071044921875 0.34600830078125 0.882238769531246 +1.64025 0.04071044921875 0.34600830078125 0.882238769531246 +1.6403750000000002 0.0411376953125 0.349761962890625 0.882238769531246 +1.6405 0.041595458984375 0.353485107421875 0.882238769531246 +1.640625 0.041595458984375 0.353485107421875 0.882238769531246 +1.64075 0.042022705078125 0.357177734375 0.882238769531246 +1.640875 0.042022705078125 0.357177734375 0.882238769531246 +1.641 0.042449951171875 0.360809326171875 0.882238769531246 +1.641125 0.042877197265625 0.36444091796875 0.882238769531246 +1.64125 0.042877197265625 0.36444091796875 0.882238769531246 +1.641375 0.043304443359375 0.36798095703125 0.882238769531246 +1.6415 0.043304443359375 0.36798095703125 0.882238769531246 +1.6416250000000002 0.043701171875 0.37152099609375 0.882238769531246 +1.64175 0.04412841796875 0.375 0.882238769531246 +1.641875 0.04412841796875 0.375 0.882238769531246 +1.6420000000000002 0.044525146484375 0.378448486328125 0.882238769531246 +1.642125 0.044525146484375 0.378448486328125 0.882238769531246 +1.64225 0.044921875 0.3818359375 0.882238769531246 +1.6423750000000002 0.045318603515625 0.38519287109375 0.882238769531246 +1.6425 0.045318603515625 0.38519287109375 0.882238769531246 +1.642625 0.04571533203125 0.388519287109375 0.882238769531246 +1.64275 0.04571533203125 0.388519287109375 0.882238769531246 +1.642875 0.04608154296875 0.39178466796875 0.882238769531246 +1.643 0.046478271484375 0.39501953125 0.882238769531246 +1.643125 0.046478271484375 0.39501953125 0.882238769531246 +1.64325 0.046844482421875 0.398193359375 0.882238769531246 +1.643375 0.046844482421875 0.398193359375 0.882238769531246 +1.6435 0.047210693359375 0.401336669921875 0.882238769531246 +1.6436250000000002 0.047576904296875 0.404449462890625 0.882238769531246 +1.64375 0.047576904296875 0.404449462890625 0.882238769531246 +1.643875 0.047943115234375 0.407501220703125 0.882238769531246 +1.6440000000000002 0.047943115234375 0.407501220703125 0.882238769531246 +1.644125 0.048309326171875 0.4105224609375 0.882238769531246 +1.64425 0.04864501953125 0.413482666015625 0.882238769531246 +1.6443750000000002 0.04864501953125 0.413482666015625 0.882238769531246 +1.6445 0.048980712890625 0.416412353515625 0.882238769531246 +1.644625 0.048980712890625 0.416412353515625 0.882238769531246 +1.64475 0.04931640625 0.419281005859375 0.882238769531246 +1.644875 0.049652099609375 0.422119140625 0.882238769531246 +1.645 0.049652099609375 0.422119140625 0.882238769531246 +1.645125 0.04998779296875 0.424896240234375 0.882238769531246 +1.64525 0.04998779296875 0.424896240234375 0.882238769531246 +1.645375 0.050323486328125 0.4276123046875 0.882238769531246 +1.6455 0.050628662109375 0.4302978515625 0.882238769531246 +1.6456250000000002 0.050628662109375 0.4302978515625 0.882238769531246 +1.64575 0.050933837890625 0.432952880859375 0.882238769531246 +1.645875 0.050933837890625 0.432952880859375 0.882238769531246 +1.6460000000000002 0.051239013671875 0.435546875 0.882238769531246 +1.646125 0.051544189453125 0.438079833984375 0.882238769531246 +1.64625 0.051544189453125 0.438079833984375 0.882238769531246 +1.6463750000000002 0.051849365234375 0.440582275390625 0.882238769531246 +1.6465 0.051849365234375 0.440582275390625 0.882238769531246 +1.646625 0.0521240234375 0.44305419921875 0.882238769531246 +1.64675 0.052398681640625 0.4454345703125 0.882238769531246 +1.646875 0.052398681640625 0.4454345703125 0.882238769531246 +1.647 0.05267333984375 0.447784423828125 0.882238769531246 +1.647125 0.05267333984375 0.447784423828125 0.882238769531246 +1.64725 0.052947998046875 0.450103759765625 0.882238769531246 +1.647375 0.05322265625 0.452362060546875 0.882238769531246 +1.6475 0.05322265625 0.452362060546875 0.882238769531246 +1.6476250000000002 0.053466796875 0.454559326171875 0.882238769531246 +1.64775 0.053466796875 0.454559326171875 0.882238769531246 +1.647875 0.053741455078125 0.45672607421875 0.882238769531246 +1.6480000000000002 0.053985595703125 0.458831787109375 0.882238769531246 +1.648125 0.053985595703125 0.458831787109375 0.882238769531246 +1.64825 0.054229736328125 0.46087646484375 0.882238769531246 +1.648375 0.054229736328125 0.46087646484375 0.882238769531246 +1.6485 0.054473876953125 0.462890625 0.882238769531246 +1.648625 0.0546875 0.464813232421875 0.882238769531246 +1.64875 0.0546875 0.464813232421875 0.882238769531246 +1.648875 0.054901123046875 0.46673583984375 0.882238769531246 +1.649 0.054901123046875 0.46673583984375 0.882238769531246 +1.649125 0.05511474609375 0.46856689453125 0.882238769531246 +1.64925 0.055328369140625 0.470367431640625 0.882238769531246 +1.649375 0.055328369140625 0.470367431640625 0.882238769531246 +1.6495 0.0555419921875 0.472137451171875 0.882238769531246 +1.6496250000000002 0.0555419921875 0.472137451171875 0.882238769531246 +1.64975 0.055755615234375 0.47381591796875 0.882238769531246 +1.649875 0.055938720703125 0.4754638671875 0.882238769531246 +1.6500000000000002 0.055938720703125 0.4754638671875 0.882238769531246 +1.650125 0.056121826171875 0.47705078125 0.882238769531246 +1.65025 0.056121826171875 0.47705078125 0.882238769531246 +1.650375 0.056304931640625 0.478607177734375 0.882238769531246 +1.6505 0.056488037109375 0.480072021484375 0.882238769531246 +1.650625 0.056488037109375 0.480072021484375 0.882238769531246 +1.65075 0.056640625 0.48150634765625 0.882238769531246 +1.650875 0.056640625 0.48150634765625 0.882238769531246 +1.651 0.05682373046875 0.48291015625 0.882238769531246 +1.651125 0.056976318359375 0.484222412109375 0.882238769531246 +1.65125 0.056976318359375 0.484222412109375 0.882238769531246 +1.651375 0.05712890625 0.485504150390625 0.882238769531246 +1.6515 0.05712890625 0.485504150390625 0.882238769531246 +1.6516250000000002 0.0572509765625 0.486724853515625 0.882238769531246 +1.65175 0.057403564453125 0.487884521484375 0.882238769531246 +1.651875 0.057403564453125 0.487884521484375 0.882238769531246 +1.6520000000000002 0.057525634765625 0.489013671875 0.882238769531246 +1.652125 0.057525634765625 0.489013671875 0.882238769531246 +1.65225 0.057647705078125 0.490081787109375 0.882238769531246 +1.652375 0.057769775390625 0.4910888671875 0.882238769531246 +1.6525 0.057769775390625 0.4910888671875 0.882238769531246 +1.652625 0.057891845703125 0.492034912109375 0.882238769531246 +1.65275 0.057891845703125 0.492034912109375 0.882238769531246 +1.652875 0.0579833984375 0.492950439453125 0.882238769531246 +1.653 0.05810546875 0.4937744140625 0.882238769531246 +1.653125 0.05810546875 0.4937744140625 0.882238769531246 +1.65325 0.058197021484375 0.49456787109375 0.882238769531246 +1.653375 0.058197021484375 0.49456787109375 0.882238769531246 +1.6535 0.05828857421875 0.49530029296875 0.882238769531246 +1.6536250000000002 0.058349609375 0.496002197265625 0.882238769531246 +1.65375 0.058349609375 0.496002197265625 0.882238769531246 +1.653875 0.058441162109375 0.496612548828125 0.882238769531246 +1.6540000000000002 0.058441162109375 0.496612548828125 0.882238769531246 +1.654125 0.058502197265625 0.4971923828125 0.882238769531246 +1.65425 0.058563232421875 0.497711181640625 0.882238769531246 +1.654375 0.058563232421875 0.497711181640625 0.882238769531246 +1.6545 0.058624267578125 0.498199462890625 0.882238769531246 +1.654625 0.058624267578125 0.498199462890625 0.882238769531246 +1.65475 0.05865478515625 0.49859619140625 0.882238769531246 +1.654875 0.0587158203125 0.49896240234375 0.882238769531246 +1.655 0.0587158203125 0.49896240234375 0.882238769531246 +1.655125 0.058746337890625 0.499267578125 0.882238769531246 +1.65525 0.058746337890625 0.499267578125 0.882238769531246 +1.655375 0.05877685546875 0.49951171875 0.882238769531246 +1.6555 0.05877685546875 0.49969482421875 0.882238769531246 +1.6556250000000002 0.05877685546875 0.49969482421875 0.882238769531246 +1.65575 0.058807373046875 0.49981689453125 0.882238769531246 +1.655875 0.058807373046875 0.49981689453125 0.882238769531246 +1.6560000000000002 0.058807373046875 0.499908447265625 0.882238769531246 +1.656125 0.058807373046875 0.49993896484375 0.882238769531246 +1.65625 0.058807373046875 0.49993896484375 0.882238769531246 +1.656375 0.058807373046875 0.499908447265625 0.882238769531246 +1.6565 0.058807373046875 0.499908447265625 0.882238769531246 +1.656625 0.058807373046875 0.49981689453125 0.882238769531246 +1.65675 0.05877685546875 0.49969482421875 0.882238769531246 +1.656875 0.05877685546875 0.49969482421875 0.882238769531246 +1.657 0.05877685546875 0.49951171875 0.882238769531246 +1.657125 0.05877685546875 0.49951171875 0.882238769531246 +1.6572500000000002 0.058746337890625 0.499267578125 0.882238769531246 +1.657375 0.0587158203125 0.49896240234375 0.882238769531246 +1.6575 0.0587158203125 0.49896240234375 0.882238769531246 +1.6576250000000002 0.05865478515625 0.49859619140625 0.882238769531246 +1.65775 0.05865478515625 0.49859619140625 0.882238769531246 +1.657875 0.058624267578125 0.498199462890625 0.882238769531246 +1.6580000000000002 0.058563232421875 0.497711181640625 0.882238769531246 +1.658125 0.058563232421875 0.497711181640625 0.882238769531246 +1.65825 0.058502197265625 0.4971923828125 0.882238769531246 +1.658375 0.058502197265625 0.4971923828125 0.882238769531246 +1.6585 0.058441162109375 0.496612548828125 0.882238769531246 +1.658625 0.058349609375 0.496002197265625 0.882238769531246 +1.65875 0.058349609375 0.496002197265625 0.882238769531246 +1.658875 0.05828857421875 0.49530029296875 0.882238769531246 +1.659 0.05828857421875 0.49530029296875 0.882238769531246 +1.659125 0.058197021484375 0.49456787109375 0.882238769531246 +1.6592500000000002 0.05810546875 0.4937744140625 0.882238769531246 +1.659375 0.05810546875 0.4937744140625 0.882238769531246 +1.6595 0.0579833984375 0.492950439453125 0.882238769531246 +1.6596250000000002 0.0579833984375 0.492950439453125 0.882238769531246 +1.65975 0.057891845703125 0.492034912109375 0.882238769531246 +1.659875 0.057769775390625 0.4910888671875 0.882238769531246 +1.6600000000000002 0.057769775390625 0.4910888671875 0.882238769531246 +1.660125 0.057647705078125 0.490081787109375 0.882238769531246 +1.66025 0.057647705078125 0.490081787109375 0.882238769531246 +1.660375 0.057525634765625 0.489013671875 0.882238769531246 +1.6605 0.057403564453125 0.487884521484375 0.882238769531246 +1.660625 0.057403564453125 0.487884521484375 0.882238769531246 +1.66075 0.0572509765625 0.486724853515625 0.882238769531246 +1.660875 0.0572509765625 0.486724853515625 0.882238769531246 +1.661 0.05712890625 0.485504150390625 0.882238769531246 +1.661125 0.056976318359375 0.484222412109375 0.882238769531246 +1.6612500000000002 0.056976318359375 0.484222412109375 0.882238769531246 +1.661375 0.05682373046875 0.48291015625 0.882238769531246 +1.6615 0.05682373046875 0.48291015625 0.882238769531246 +1.6616250000000002 0.056640625 0.48150634765625 0.882238769531246 +1.66175 0.056488037109375 0.480072021484375 0.882238769531246 +1.661875 0.056488037109375 0.480072021484375 0.882238769531246 +1.6620000000000002 0.056304931640625 0.478607177734375 0.882238769531246 +1.662125 0.056304931640625 0.478607177734375 0.882238769531246 +1.66225 0.056121826171875 0.47705078125 0.882238769531246 +1.662375 0.055938720703125 0.4754638671875 0.882238769531246 +1.6625 0.055938720703125 0.4754638671875 0.882238769531246 +1.662625 0.055755615234375 0.47381591796875 0.882238769531246 +1.66275 0.055755615234375 0.47381591796875 0.882238769531246 +1.662875 0.0555419921875 0.472137451171875 0.882238769531246 +1.663 0.055328369140625 0.470367431640625 0.882238769531246 +1.663125 0.055328369140625 0.470367431640625 0.882238769531246 +1.6632500000000002 0.05511474609375 0.46856689453125 0.882238769531246 +1.663375 0.05511474609375 0.46856689453125 0.882238769531246 +1.6635 0.054901123046875 0.46673583984375 0.882238769531246 +1.6636250000000002 0.0546875 0.464813232421875 0.882238769531246 +1.66375 0.0546875 0.464813232421875 0.882238769531246 +1.663875 0.054473876953125 0.462890625 0.882238769531246 +1.6640000000000002 0.17230224609375 0.462890625 0.6276428222656184 +1.664125 0.17156982421875 0.46087646484375 0.6276428222656184 +1.66425 0.170806884765625 0.458831787109375 0.6276428222656184 +1.664375 0.170806884765625 0.458831787109375 0.6276428222656184 +1.6645 0.170013427734375 0.45672607421875 0.6276428222656184 +1.664625 0.170013427734375 0.45672607421875 0.6276428222656184 +1.66475 0.169219970703125 0.454559326171875 0.6276428222656184 +1.664875 0.16839599609375 0.452362060546875 0.6276428222656184 +1.665 0.16839599609375 0.452362060546875 0.6276428222656184 +1.665125 0.16754150390625 0.450103759765625 0.6276428222656184 +1.6652500000000002 0.16754150390625 0.450103759765625 0.6276428222656184 +1.665375 0.16668701171875 0.447784423828125 0.6276428222656184 +1.6655 0.165802001953125 0.4454345703125 0.6276428222656184 +1.6656250000000002 0.165802001953125 0.4454345703125 0.6276428222656184 +1.66575 0.1649169921875 0.44305419921875 0.6276428222656184 +1.665875 0.1649169921875 0.44305419921875 0.6276428222656184 +1.666 0.16400146484375 0.440582275390625 0.6276428222656184 +1.666125 0.1630859375 0.438079833984375 0.6276428222656184 +1.66625 0.1630859375 0.438079833984375 0.6276428222656184 +1.666375 0.162139892578125 0.435546875 0.6276428222656184 +1.6665 0.162139892578125 0.435546875 0.6276428222656184 +1.666625 0.161163330078125 0.432952880859375 0.6276428222656184 +1.66675 0.160186767578125 0.4302978515625 0.6276428222656184 +1.666875 0.160186767578125 0.4302978515625 0.6276428222656184 +1.667 0.1591796875 0.4276123046875 0.6276428222656184 +1.667125 0.1591796875 0.4276123046875 0.6276428222656184 +1.6672500000000002 0.158172607421875 0.424896240234375 0.6276428222656184 +1.667375 0.157135009765625 0.422119140625 0.6276428222656184 +1.6675 0.157135009765625 0.422119140625 0.6276428222656184 +1.6676250000000002 0.15606689453125 0.419281005859375 0.6276428222656184 +1.66775 0.15606689453125 0.419281005859375 0.6276428222656184 +1.667875 0.154998779296875 0.416412353515625 0.6276428222656184 +1.668 0.1539306640625 0.413482666015625 0.6276428222656184 +1.668125 0.1539306640625 0.413482666015625 0.6276428222656184 +1.66825 0.152801513671875 0.4105224609375 0.6276428222656184 +1.668375 0.152801513671875 0.4105224609375 0.6276428222656184 +1.6685 0.151702880859375 0.407501220703125 0.6276428222656184 +1.668625 0.15057373046875 0.404449462890625 0.6276428222656184 +1.66875 0.15057373046875 0.404449462890625 0.6276428222656184 +1.668875 0.1494140625 0.401336669921875 0.6276428222656184 +1.669 0.1494140625 0.401336669921875 0.6276428222656184 +1.669125 0.148223876953125 0.398193359375 0.6276428222656184 +1.6692500000000002 0.147064208984375 0.39501953125 0.6276428222656184 +1.669375 0.147064208984375 0.39501953125 0.6276428222656184 +1.6695 0.145843505859375 0.39178466796875 0.6276428222656184 +1.6696250000000002 0.145843505859375 0.39178466796875 0.6276428222656184 +1.66975 0.144622802734375 0.388519287109375 0.6276428222656184 +1.669875 0.143402099609375 0.38519287109375 0.6276428222656184 +1.67 0.143402099609375 0.38519287109375 0.6276428222656184 +1.670125 0.14215087890625 0.3818359375 0.6276428222656184 +1.67025 0.14215087890625 0.3818359375 0.6276428222656184 +1.670375 0.140869140625 0.378448486328125 0.6276428222656184 +1.6705 0.13958740234375 0.375 0.6276428222656184 +1.670625 0.13958740234375 0.375 0.6276428222656184 +1.67075 0.1383056640625 0.37152099609375 0.6276428222656184 +1.670875 0.1383056640625 0.37152099609375 0.6276428222656184 +1.671 0.136993408203125 0.36798095703125 0.6276428222656184 +1.671125 0.135650634765625 0.36444091796875 0.6276428222656184 +1.6712500000000002 0.135650634765625 0.36444091796875 0.6276428222656184 +1.671375 0.134307861328125 0.360809326171875 0.6276428222656184 +1.6715 0.134307861328125 0.360809326171875 0.6276428222656184 +1.6716250000000002 0.132965087890625 0.357177734375 0.6276428222656184 +1.67175 0.131591796875 0.353485107421875 0.6276428222656184 +1.671875 0.131591796875 0.353485107421875 0.6276428222656184 +1.672 0.13018798828125 0.349761962890625 0.6276428222656184 +1.672125 0.13018798828125 0.349761962890625 0.6276428222656184 +1.67225 0.128814697265625 0.34600830078125 0.6276428222656184 +1.672375 0.12738037109375 0.34222412109375 0.6276428222656184 +1.6725 0.12738037109375 0.34222412109375 0.6276428222656184 +1.672625 0.1259765625 0.33837890625 0.6276428222656184 +1.67275 0.1259765625 0.33837890625 0.6276428222656184 +1.6728750000000002 0.12451171875 0.334503173828125 0.6276428222656184 +1.673 0.123077392578125 0.330596923828125 0.6276428222656184 +1.673125 0.123077392578125 0.330596923828125 0.6276428222656184 +1.6732500000000002 0.12158203125 0.32666015625 0.6276428222656184 +1.673375 0.12158203125 0.32666015625 0.6276428222656184 +1.6735 0.1201171875 0.322662353515625 0.6276428222656184 +1.6736250000000002 0.118621826171875 0.31866455078125 0.6276428222656184 +1.67375 0.118621826171875 0.31866455078125 0.6276428222656184 +1.673875 0.117095947265625 0.314605712890625 0.6276428222656184 +1.674 0.117095947265625 0.314605712890625 0.6276428222656184 +1.674125 0.1156005859375 0.310516357421875 0.6276428222656184 +1.67425 0.114044189453125 0.306396484375 0.6276428222656184 +1.674375 0.114044189453125 0.306396484375 0.6276428222656184 +1.6745 0.112518310546875 0.30224609375 0.6276428222656184 +1.674625 0.112518310546875 0.30224609375 0.6276428222656184 +1.67475 0.1109619140625 0.298065185546875 0.6276428222656184 +1.6748750000000002 0.109375 0.2938232421875 0.6276428222656184 +1.675 0.109375 0.2938232421875 0.6276428222656184 +1.675125 0.1077880859375 0.289581298828125 0.6276428222656184 +1.6752500000000002 0.1077880859375 0.289581298828125 0.6276428222656184 +1.675375 0.106201171875 0.285308837890625 0.6276428222656184 +1.6755 0.104583740234375 0.280975341796875 0.6276428222656184 +1.6756250000000002 0.104583740234375 0.280975341796875 0.6276428222656184 +1.67575 0.10296630859375 0.276641845703125 0.6276428222656184 +1.675875 0.10296630859375 0.276641845703125 0.6276428222656184 +1.676 0.101348876953125 0.27227783203125 0.6276428222656184 +1.676125 0.099700927734375 0.267852783203125 0.6276428222656184 +1.67625 0.099700927734375 0.267852783203125 0.6276428222656184 +1.676375 0.098052978515625 0.263427734375 0.6276428222656184 +1.6765 0.098052978515625 0.263427734375 0.6276428222656184 +1.676625 0.096405029296875 0.25897216796875 0.6276428222656184 +1.67675 0.0947265625 0.25445556640625 0.6276428222656184 +1.6768750000000002 0.0947265625 0.25445556640625 0.6276428222656184 +1.677 0.093048095703125 0.249969482421875 0.6276428222656184 +1.677125 0.093048095703125 0.249969482421875 0.6276428222656184 +1.6772500000000002 0.091339111328125 0.24542236328125 0.6276428222656184 +1.677375 0.08966064453125 0.2408447265625 0.6276428222656184 +1.6775 0.08966064453125 0.2408447265625 0.6276428222656184 +1.6776250000000002 0.087921142578125 0.236236572265625 0.6276428222656184 +1.67775 0.087921142578125 0.236236572265625 0.6276428222656184 +1.677875 0.086212158203125 0.231597900390625 0.6276428222656184 +1.678 0.08447265625 0.226959228515625 0.6276428222656184 +1.678125 0.08447265625 0.226959228515625 0.6276428222656184 +1.67825 0.082733154296875 0.2222900390625 0.6276428222656184 +1.678375 0.082733154296875 0.2222900390625 0.6276428222656184 +1.6785 0.08099365234375 0.21759033203125 0.6276428222656184 +1.678625 0.0792236328125 0.212860107421875 0.6276428222656184 +1.67875 0.0792236328125 0.212860107421875 0.6276428222656184 +1.6788750000000002 0.07745361328125 0.208099365234375 0.6276428222656184 +1.679 0.07745361328125 0.208099365234375 0.6276428222656184 +1.679125 0.07568359375 0.203338623046875 0.6276428222656184 +1.6792500000000002 0.07391357421875 0.19854736328125 0.6276428222656184 +1.679375 0.07391357421875 0.19854736328125 0.6276428222656184 +1.6795 0.072113037109375 0.1937255859375 0.6276428222656184 +1.6796250000000002 0.072113037109375 0.1937255859375 0.6276428222656184 +1.67975 0.0703125 0.188873291015625 0.6276428222656184 +1.679875 0.068511962890625 0.18402099609375 0.6276428222656184 +1.68 0.068511962890625 0.18402099609375 0.6276428222656184 +1.680125 0.066680908203125 0.17913818359375 0.6276428222656184 +1.68025 0.066680908203125 0.17913818359375 0.6276428222656184 +1.680375 0.064849853515625 0.17425537109375 0.6276428222656184 +1.6805 0.063018798828125 0.169342041015625 0.6276428222656184 +1.680625 0.063018798828125 0.169342041015625 0.6276428222656184 +1.68075 0.061187744140625 0.164398193359375 0.6276428222656184 +1.6808750000000002 0.061187744140625 0.164398193359375 0.6276428222656184 +1.681 0.059356689453125 0.159454345703125 0.6276428222656184 +1.681125 0.0574951171875 0.15447998046875 0.6276428222656184 +1.6812500000000002 0.0574951171875 0.15447998046875 0.6276428222656184 +1.681375 0.055633544921875 0.14947509765625 0.6276428222656184 +1.6815 0.055633544921875 0.14947509765625 0.6276428222656184 +1.681625 0.05377197265625 0.14447021484375 0.6276428222656184 +1.68175 0.051910400390625 0.13946533203125 0.6276428222656184 +1.681875 0.051910400390625 0.13946533203125 0.6276428222656184 +1.682 0.050018310546875 0.134429931640625 0.6276428222656184 +1.682125 0.050018310546875 0.134429931640625 0.6276428222656184 +1.68225 0.04815673828125 0.129364013671875 0.6276428222656184 +1.682375 0.0462646484375 0.124298095703125 0.6276428222656184 +1.6825 0.0462646484375 0.124298095703125 0.6276428222656184 +1.682625 0.04437255859375 0.119232177734375 0.6276428222656184 +1.68275 0.04437255859375 0.119232177734375 0.6276428222656184 +1.6828750000000002 0.04248046875 0.1141357421875 0.6276428222656184 +1.683 0.04058837890625 0.109039306640625 0.6276428222656184 +1.683125 0.04058837890625 0.109039306640625 0.6276428222656184 +1.6832500000000002 0.038665771484375 0.103912353515625 0.6276428222656184 +1.683375 0.038665771484375 0.103912353515625 0.6276428222656184 +1.6835 0.036773681640625 0.098785400390625 0.6276428222656184 +1.683625 0.03485107421875 0.093658447265625 0.6276428222656184 +1.68375 0.03485107421875 0.093658447265625 0.6276428222656184 +1.683875 0.032928466796875 0.0885009765625 0.6276428222656184 +1.684 0.032928466796875 0.0885009765625 0.6276428222656184 +1.684125 0.031005859375 0.083343505859375 0.6276428222656184 +1.68425 0.029083251953125 0.07818603515625 0.6276428222656184 +1.684375 0.029083251953125 0.07818603515625 0.6276428222656184 +1.6845 0.02716064453125 0.072998046875 0.6276428222656184 +1.684625 0.02716064453125 0.072998046875 0.6276428222656184 +1.68475 0.025238037109375 0.06781005859375 0.6276428222656184 +1.6848750000000002 0.0233154296875 0.0626220703125 0.6276428222656184 +1.685 0.0233154296875 0.0626220703125 0.6276428222656184 +1.685125 0.0213623046875 0.05743408203125 0.6276428222656184 +1.6852500000000002 0.0213623046875 0.05743408203125 0.6276428222656184 +1.685375 0.019439697265625 0.05224609375 0.6276428222656184 +1.6855 0.017486572265625 0.047027587890625 0.6276428222656184 +1.685625 0.017486572265625 0.047027587890625 0.6276428222656184 +1.68575 0.01556396484375 0.04180908203125 0.6276428222656184 +1.685875 0.01556396484375 0.04180908203125 0.6276428222656184 +1.686 0.01361083984375 0.036590576171875 0.6276428222656184 +1.686125 0.01165771484375 0.0313720703125 0.6276428222656184 +1.68625 0.01165771484375 0.0313720703125 0.6276428222656184 +1.686375 0.009735107421875 0.026123046875 0.6276428222656184 +1.6865 0.009735107421875 0.026123046875 0.6276428222656184 +1.686625 0.007781982421875 0.020904541015625 0.6276428222656184 +1.68675 0.005828857421875 0.01568603515625 0.6276428222656184 +1.6868750000000002 0.005828857421875 0.01568603515625 0.6276428222656184 +1.687 0.003875732421875 0.01043701171875 0.6276428222656184 +1.687125 0.003875732421875 0.01043701171875 0.6276428222656184 +1.6872500000000002 0.001922607421875 0.005218505859375 0.6276428222656184 +1.687375 0.0 0.0 0.6276428222656184 +1.6875 0.0 0.0 0.6276428222656184 +1.687625 -0.001922607421875 -0.005218505859375 0.6276428222656184 +1.68775 -0.001922607421875 -0.005218505859375 0.6276428222656184 +1.687875 -0.003875732421875 -0.01043701171875 0.6276428222656184 +1.688 -0.005828857421875 -0.01568603515625 0.6276428222656184 +1.688125 -0.005828857421875 -0.01568603515625 0.6276428222656184 +1.68825 -0.007781982421875 -0.020904541015625 0.6276428222656184 +1.688375 -0.007781982421875 -0.020904541015625 0.6276428222656184 +1.6885000000000002 -0.009735107421875 -0.026123046875 0.6276428222656184 +1.688625 -0.01165771484375 -0.0313720703125 0.6276428222656184 +1.68875 -0.01165771484375 -0.0313720703125 0.6276428222656184 +1.6888750000000002 -0.01361083984375 -0.036590576171875 0.6276428222656184 +1.689 -0.01361083984375 -0.036590576171875 0.6276428222656184 +1.689125 -0.01556396484375 -0.04180908203125 0.6276428222656184 +1.6892500000000002 -0.017486572265625 -0.047027587890625 0.6276428222656184 +1.689375 -0.017486572265625 -0.047027587890625 0.6276428222656184 +1.6895 -0.019439697265625 -0.05224609375 0.6276428222656184 +1.689625 -0.019439697265625 -0.05224609375 0.6276428222656184 +1.68975 -0.0213623046875 -0.05743408203125 0.6276428222656184 +1.689875 -0.0233154296875 -0.0626220703125 0.6276428222656184 +1.69 -0.0233154296875 -0.0626220703125 0.6276428222656184 +1.690125 -0.025238037109375 -0.06781005859375 0.6276428222656184 +1.69025 -0.025238037109375 -0.06781005859375 0.6276428222656184 +1.690375 -0.02716064453125 -0.072998046875 0.6276428222656184 +1.6905000000000002 -0.029083251953125 -0.07818603515625 0.6276428222656184 +1.690625 -0.029083251953125 -0.07818603515625 0.6276428222656184 +1.69075 -0.031005859375 -0.083343505859375 0.6276428222656184 +1.6908750000000002 -0.031005859375 -0.083343505859375 0.6276428222656184 +1.691 -0.032928466796875 -0.0885009765625 0.6276428222656184 +1.691125 -0.03485107421875 -0.093658447265625 0.6276428222656184 +1.6912500000000002 -0.03485107421875 -0.093658447265625 0.6276428222656184 +1.691375 -0.036773681640625 -0.098785400390625 0.6276428222656184 +1.6915 -0.036773681640625 -0.098785400390625 0.6276428222656184 +1.691625 -0.038665771484375 -0.103912353515625 0.6276428222656184 +1.69175 -0.04058837890625 -0.109039306640625 0.6276428222656184 +1.691875 -0.04058837890625 -0.109039306640625 0.6276428222656184 +1.692 -0.04248046875 -0.1141357421875 0.6276428222656184 +1.692125 -0.04248046875 -0.1141357421875 0.6276428222656184 +1.69225 -0.04437255859375 -0.119232177734375 0.6276428222656184 +1.692375 -0.0462646484375 -0.124298095703125 0.6276428222656184 +1.6925000000000002 -0.0462646484375 -0.124298095703125 0.6276428222656184 +1.692625 -0.04815673828125 -0.129364013671875 0.6276428222656184 +1.69275 -0.04815673828125 -0.129364013671875 0.6276428222656184 +1.6928750000000002 -0.050018310546875 -0.134429931640625 0.6276428222656184 +1.693 -0.051910400390625 -0.13946533203125 0.6276428222656184 +1.693125 -0.051910400390625 -0.13946533203125 0.6276428222656184 +1.6932500000000002 -0.05377197265625 -0.14447021484375 0.6276428222656184 +1.693375 -0.05377197265625 -0.14447021484375 0.6276428222656184 +1.6935 -0.055633544921875 -0.14947509765625 0.6276428222656184 +1.693625 -0.0574951171875 -0.15447998046875 0.6276428222656184 +1.69375 -0.0574951171875 -0.15447998046875 0.6276428222656184 +1.693875 -0.059356689453125 -0.159454345703125 0.6276428222656184 +1.694 -0.059356689453125 -0.159454345703125 0.6276428222656184 +1.694125 -0.061187744140625 -0.164398193359375 0.6276428222656184 +1.69425 -0.063018798828125 -0.169342041015625 0.6276428222656184 +1.694375 -0.063018798828125 -0.169342041015625 0.6276428222656184 +1.6945000000000002 -0.064849853515625 -0.17425537109375 0.6276428222656184 +1.694625 -0.064849853515625 -0.17425537109375 0.6276428222656184 +1.69475 -0.066680908203125 -0.17913818359375 0.6276428222656184 +1.6948750000000002 -0.068511962890625 -0.18402099609375 0.6276428222656184 +1.695 -0.068511962890625 -0.18402099609375 0.6276428222656184 +1.695125 -0.0703125 -0.188873291015625 0.6276428222656184 +1.6952500000000002 -0.0703125 -0.188873291015625 0.6276428222656184 +1.695375 -0.072113037109375 -0.1937255859375 0.6276428222656184 +1.6955 -0.07391357421875 -0.19854736328125 0.6276428222656184 +1.695625 -0.07391357421875 -0.19854736328125 0.6276428222656184 +1.69575 -0.07568359375 -0.203338623046875 0.6276428222656184 +1.695875 -0.07568359375 -0.203338623046875 0.6276428222656184 +1.696 -0.15130615234375 -0.208099365234375 0.2729187011718668 +1.696125 -0.154754638671875 -0.212860107421875 0.2729187011718668 +1.69625 -0.154754638671875 -0.212860107421875 0.2729187011718668 +1.696375 -0.158172607421875 -0.21759033203125 0.2729187011718668 +1.6965000000000002 -0.158172607421875 -0.21759033203125 0.2729187011718668 +1.696625 -0.161590576171875 -0.2222900390625 0.2729187011718668 +1.69675 -0.16497802734375 -0.226959228515625 0.2729187011718668 +1.6968750000000002 -0.16497802734375 -0.226959228515625 0.2729187011718668 +1.697 -0.168365478515625 -0.231597900390625 0.2729187011718668 +1.697125 -0.168365478515625 -0.231597900390625 0.2729187011718668 +1.69725 -0.1717529296875 -0.236236572265625 0.2729187011718668 +1.697375 -0.175079345703125 -0.2408447265625 0.2729187011718668 +1.6975 -0.175079345703125 -0.2408447265625 0.2729187011718668 +1.697625 -0.17840576171875 -0.24542236328125 0.2729187011718668 +1.69775 -0.17840576171875 -0.24542236328125 0.2729187011718668 +1.697875 -0.181732177734375 -0.249969482421875 0.2729187011718668 +1.698 -0.18499755859375 -0.25445556640625 0.2729187011718668 +1.698125 -0.18499755859375 -0.25445556640625 0.2729187011718668 +1.69825 -0.188262939453125 -0.25897216796875 0.2729187011718668 +1.698375 -0.188262939453125 -0.25897216796875 0.2729187011718668 +1.6985000000000002 -0.1915283203125 -0.263427734375 0.2729187011718668 +1.698625 -0.194732666015625 -0.267852783203125 0.2729187011718668 +1.69875 -0.194732666015625 -0.267852783203125 0.2729187011718668 +1.6988750000000002 -0.19793701171875 -0.27227783203125 0.2729187011718668 +1.699 -0.19793701171875 -0.27227783203125 0.2729187011718668 +1.699125 -0.20111083984375 -0.276641845703125 0.2729187011718668 +1.69925 -0.20428466796875 -0.280975341796875 0.2729187011718668 +1.699375 -0.20428466796875 -0.280975341796875 0.2729187011718668 +1.6995 -0.207427978515625 -0.285308837890625 0.2729187011718668 +1.699625 -0.207427978515625 -0.285308837890625 0.2729187011718668 +1.69975 -0.210540771484375 -0.289581298828125 0.2729187011718668 +1.699875 -0.213623046875 -0.2938232421875 0.2729187011718668 +1.7 -0.213623046875 -0.2938232421875 0.2729187011718668 +1.700125 -0.216705322265625 -0.298065185546875 0.2729187011718668 +1.70025 -0.216705322265625 -0.298065185546875 0.2729187011718668 +1.700375 -0.2197265625 -0.30224609375 0.2729187011718668 +1.7005000000000002 -0.2227783203125 -0.306396484375 0.2729187011718668 +1.700625 -0.2227783203125 -0.306396484375 0.2729187011718668 +1.70075 -0.22576904296875 -0.310516357421875 0.2729187011718668 +1.7008750000000002 -0.22576904296875 -0.310516357421875 0.2729187011718668 +1.701 -0.228729248046875 -0.314605712890625 0.2729187011718668 +1.701125 -0.231658935546875 -0.31866455078125 0.2729187011718668 +1.70125 -0.231658935546875 -0.31866455078125 0.2729187011718668 +1.701375 -0.234588623046875 -0.322662353515625 0.2729187011718668 +1.7015 -0.234588623046875 -0.322662353515625 0.2729187011718668 +1.701625 -0.23748779296875 -0.32666015625 0.2729187011718668 +1.70175 -0.2403564453125 -0.330596923828125 0.2729187011718668 +1.701875 -0.2403564453125 -0.330596923828125 0.2729187011718668 +1.702 -0.243194580078125 -0.334503173828125 0.2729187011718668 +1.702125 -0.243194580078125 -0.334503173828125 0.2729187011718668 +1.70225 -0.246002197265625 -0.33837890625 0.2729187011718668 +1.702375 -0.248809814453125 -0.34222412109375 0.2729187011718668 +1.7025000000000002 -0.248809814453125 -0.34222412109375 0.2729187011718668 +1.702625 -0.251556396484375 -0.34600830078125 0.2729187011718668 +1.70275 -0.251556396484375 -0.34600830078125 0.2729187011718668 +1.7028750000000002 -0.254302978515625 -0.349761962890625 0.2729187011718668 +1.703 -0.256988525390625 -0.353485107421875 0.2729187011718668 +1.703125 -0.256988525390625 -0.353485107421875 0.2729187011718668 +1.70325 -0.259674072265625 -0.357177734375 0.2729187011718668 +1.703375 -0.259674072265625 -0.357177734375 0.2729187011718668 +1.7035 -0.2623291015625 -0.360809326171875 0.2729187011718668 +1.703625 -0.26495361328125 -0.36444091796875 0.2729187011718668 +1.70375 -0.26495361328125 -0.36444091796875 0.2729187011718668 +1.703875 -0.267547607421875 -0.36798095703125 0.2729187011718668 +1.704 -0.267547607421875 -0.36798095703125 0.2729187011718668 +1.7041250000000002 -0.270111083984375 -0.37152099609375 0.2729187011718668 +1.70425 -0.272613525390625 -0.375 0.2729187011718668 +1.704375 -0.272613525390625 -0.375 0.2729187011718668 +1.7045000000000002 -0.275146484375 -0.378448486328125 0.2729187011718668 +1.704625 -0.275146484375 -0.378448486328125 0.2729187011718668 +1.70475 -0.277618408203125 -0.3818359375 0.2729187011718668 +1.7048750000000002 -0.280059814453125 -0.38519287109375 0.2729187011718668 +1.705 -0.280059814453125 -0.38519287109375 0.2729187011718668 +1.705125 -0.282470703125 -0.388519287109375 0.2729187011718668 +1.70525 -0.282470703125 -0.388519287109375 0.2729187011718668 +1.705375 -0.28485107421875 -0.39178466796875 0.2729187011718668 +1.7055 -0.287200927734375 -0.39501953125 0.2729187011718668 +1.705625 -0.287200927734375 -0.39501953125 0.2729187011718668 +1.70575 -0.28948974609375 -0.398193359375 0.2729187011718668 +1.705875 -0.28948974609375 -0.398193359375 0.2729187011718668 +1.706 -0.291778564453125 -0.401336669921875 0.2729187011718668 +1.7061250000000002 -0.294036865234375 -0.404449462890625 0.2729187011718668 +1.70625 -0.294036865234375 -0.404449462890625 0.2729187011718668 +1.706375 -0.2962646484375 -0.407501220703125 0.2729187011718668 +1.7065000000000002 -0.2962646484375 -0.407501220703125 0.2729187011718668 +1.706625 -0.2984619140625 -0.4105224609375 0.2729187011718668 +1.70675 -0.30059814453125 -0.413482666015625 0.2729187011718668 +1.7068750000000002 -0.30059814453125 -0.413482666015625 0.2729187011718668 +1.707 -0.302734375 -0.416412353515625 0.2729187011718668 +1.707125 -0.302734375 -0.416412353515625 0.2729187011718668 +1.70725 -0.3048095703125 -0.419281005859375 0.2729187011718668 +1.707375 -0.306884765625 -0.422119140625 0.2729187011718668 +1.7075 -0.306884765625 -0.422119140625 0.2729187011718668 +1.707625 -0.30889892578125 -0.424896240234375 0.2729187011718668 +1.70775 -0.30889892578125 -0.424896240234375 0.2729187011718668 +1.707875 -0.310882568359375 -0.4276123046875 0.2729187011718668 +1.708 -0.312835693359375 -0.4302978515625 0.2729187011718668 +1.7081250000000002 -0.312835693359375 -0.4302978515625 0.2729187011718668 +1.70825 -0.31475830078125 -0.432952880859375 0.2729187011718668 +1.708375 -0.31475830078125 -0.432952880859375 0.2729187011718668 +1.7085000000000002 -0.316650390625 -0.435546875 0.2729187011718668 +1.708625 -0.318511962890625 -0.438079833984375 0.2729187011718668 +1.70875 -0.318511962890625 -0.438079833984375 0.2729187011718668 +1.7088750000000002 -0.3203125 -0.440582275390625 0.2729187011718668 +1.709 -0.3203125 -0.440582275390625 0.2729187011718668 +1.709125 -0.322113037109375 -0.44305419921875 0.2729187011718668 +1.70925 -0.3238525390625 -0.4454345703125 0.2729187011718668 +1.709375 -0.3238525390625 -0.4454345703125 0.2729187011718668 +1.7095 -0.3255615234375 -0.447784423828125 0.2729187011718668 +1.709625 -0.3255615234375 -0.447784423828125 0.2729187011718668 +1.70975 -0.327239990234375 -0.450103759765625 0.2729187011718668 +1.709875 -0.328857421875 -0.452362060546875 0.2729187011718668 +1.71 -0.328857421875 -0.452362060546875 0.2729187011718668 +1.7101250000000002 -0.330474853515625 -0.454559326171875 0.2729187011718668 +1.71025 -0.330474853515625 -0.454559326171875 0.2729187011718668 +1.710375 -0.33203125 -0.45672607421875 0.2729187011718668 +1.7105000000000002 -0.333587646484375 -0.458831787109375 0.2729187011718668 +1.710625 -0.333587646484375 -0.458831787109375 0.2729187011718668 +1.71075 -0.335052490234375 -0.46087646484375 0.2729187011718668 +1.7108750000000002 -0.335052490234375 -0.46087646484375 0.2729187011718668 +1.711 -0.336517333984375 -0.462890625 0.2729187011718668 +1.711125 -0.337921142578125 -0.464813232421875 0.2729187011718668 +1.71125 -0.337921142578125 -0.464813232421875 0.2729187011718668 +1.711375 -0.339324951171875 -0.46673583984375 0.2729187011718668 +1.7115 -0.339324951171875 -0.46673583984375 0.2729187011718668 +1.711625 -0.340667724609375 -0.46856689453125 0.2729187011718668 +1.71175 -0.34197998046875 -0.470367431640625 0.2729187011718668 +1.711875 -0.34197998046875 -0.470367431640625 0.2729187011718668 +1.712 -0.343231201171875 -0.472137451171875 0.2729187011718668 +1.7121250000000002 -0.343231201171875 -0.472137451171875 0.2729187011718668 +1.71225 -0.344482421875 -0.47381591796875 0.2729187011718668 +1.712375 -0.345672607421875 -0.4754638671875 0.2729187011718668 +1.7125000000000002 -0.345672607421875 -0.4754638671875 0.2729187011718668 +1.712625 -0.346832275390625 -0.47705078125 0.2729187011718668 +1.71275 -0.346832275390625 -0.47705078125 0.2729187011718668 +1.712875 -0.34796142578125 -0.478607177734375 0.2729187011718668 +1.713 -0.349029541015625 -0.480072021484375 0.2729187011718668 +1.713125 -0.349029541015625 -0.480072021484375 0.2729187011718668 +1.71325 -0.350067138671875 -0.48150634765625 0.2729187011718668 +1.713375 -0.350067138671875 -0.48150634765625 0.2729187011718668 +1.7135 -0.35107421875 -0.48291015625 0.2729187011718668 +1.713625 -0.35205078125 -0.484222412109375 0.2729187011718668 +1.71375 -0.35205078125 -0.484222412109375 0.2729187011718668 +1.713875 -0.35296630859375 -0.485504150390625 0.2729187011718668 +1.714 -0.35296630859375 -0.485504150390625 0.2729187011718668 +1.7141250000000002 -0.353851318359375 -0.486724853515625 0.2729187011718668 +1.71425 -0.354705810546875 -0.487884521484375 0.2729187011718668 +1.714375 -0.354705810546875 -0.487884521484375 0.2729187011718668 +1.7145000000000002 -0.35552978515625 -0.489013671875 0.2729187011718668 +1.714625 -0.35552978515625 -0.489013671875 0.2729187011718668 +1.71475 -0.356292724609375 -0.490081787109375 0.2729187011718668 +1.714875 -0.357025146484375 -0.4910888671875 0.2729187011718668 +1.715 -0.357025146484375 -0.4910888671875 0.2729187011718668 +1.715125 -0.35772705078125 -0.492034912109375 0.2729187011718668 +1.71525 -0.35772705078125 -0.492034912109375 0.2729187011718668 +1.715375 -0.358367919921875 -0.492950439453125 0.2729187011718668 +1.7155 -0.358978271484375 -0.4937744140625 0.2729187011718668 +1.715625 -0.358978271484375 -0.4937744140625 0.2729187011718668 +1.71575 -0.35955810546875 -0.49456787109375 0.2729187011718668 +1.715875 -0.35955810546875 -0.49456787109375 0.2729187011718668 +1.716 -0.360107421875 -0.49530029296875 0.2729187011718668 +1.7161250000000002 -0.360595703125 -0.496002197265625 0.2729187011718668 +1.71625 -0.360595703125 -0.496002197265625 0.2729187011718668 +1.716375 -0.361053466796875 -0.496612548828125 0.2729187011718668 +1.7165000000000002 -0.361053466796875 -0.496612548828125 0.2729187011718668 +1.716625 -0.361480712890625 -0.4971923828125 0.2729187011718668 +1.71675 -0.361846923828125 -0.497711181640625 0.2729187011718668 +1.716875 -0.361846923828125 -0.497711181640625 0.2729187011718668 +1.717 -0.3621826171875 -0.498199462890625 0.2729187011718668 +1.717125 -0.3621826171875 -0.498199462890625 0.2729187011718668 +1.71725 -0.36248779296875 -0.49859619140625 0.2729187011718668 +1.717375 -0.362762451171875 -0.49896240234375 0.2729187011718668 +1.7175 -0.362762451171875 -0.49896240234375 0.2729187011718668 +1.717625 -0.36297607421875 -0.499267578125 0.2729187011718668 +1.71775 -0.36297607421875 -0.499267578125 0.2729187011718668 +1.717875 -0.3631591796875 -0.49951171875 0.2729187011718668 +1.718 -0.36328125 -0.49969482421875 0.2729187011718668 +1.7181250000000002 -0.36328125 -0.49969482421875 0.2729187011718668 +1.71825 -0.363372802734375 -0.49981689453125 0.2729187011718668 +1.718375 -0.363372802734375 -0.49981689453125 0.2729187011718668 +1.7185000000000002 -0.363433837890625 -0.499908447265625 0.2729187011718668 +1.718625 -0.36346435546875 -0.49993896484375 0.2729187011718668 +1.71875 -0.36346435546875 -0.49993896484375 0.2729187011718668 +1.718875 -0.363433837890625 -0.499908447265625 0.2729187011718668 +1.719 -0.363433837890625 -0.499908447265625 0.2729187011718668 +1.719125 -0.363372802734375 -0.49981689453125 0.2729187011718668 +1.71925 -0.36328125 -0.49969482421875 0.2729187011718668 +1.719375 -0.36328125 -0.49969482421875 0.2729187011718668 +1.7195 -0.3631591796875 -0.49951171875 0.2729187011718668 +1.719625 -0.3631591796875 -0.49951171875 0.2729187011718668 +1.7197500000000002 -0.36297607421875 -0.499267578125 0.2729187011718668 +1.719875 -0.362762451171875 -0.49896240234375 0.2729187011718668 +1.72 -0.362762451171875 -0.49896240234375 0.2729187011718668 +1.7201250000000002 -0.36248779296875 -0.49859619140625 0.2729187011718668 +1.72025 -0.36248779296875 -0.49859619140625 0.2729187011718668 +1.720375 -0.3621826171875 -0.498199462890625 0.2729187011718668 +1.7205000000000002 -0.361846923828125 -0.497711181640625 0.2729187011718668 +1.720625 -0.361846923828125 -0.497711181640625 0.2729187011718668 +1.72075 -0.361480712890625 -0.4971923828125 0.2729187011718668 +1.720875 -0.361480712890625 -0.4971923828125 0.2729187011718668 +1.721 -0.361053466796875 -0.496612548828125 0.2729187011718668 +1.721125 -0.360595703125 -0.496002197265625 0.2729187011718668 +1.72125 -0.360595703125 -0.496002197265625 0.2729187011718668 +1.721375 -0.360107421875 -0.49530029296875 0.2729187011718668 +1.7215 -0.360107421875 -0.49530029296875 0.2729187011718668 +1.721625 -0.35955810546875 -0.49456787109375 0.2729187011718668 +1.7217500000000002 -0.358978271484375 -0.4937744140625 0.2729187011718668 +1.721875 -0.358978271484375 -0.4937744140625 0.2729187011718668 +1.722 -0.358367919921875 -0.492950439453125 0.2729187011718668 +1.7221250000000002 -0.358367919921875 -0.492950439453125 0.2729187011718668 +1.72225 -0.35772705078125 -0.492034912109375 0.2729187011718668 +1.722375 -0.357025146484375 -0.4910888671875 0.2729187011718668 +1.7225000000000002 -0.357025146484375 -0.4910888671875 0.2729187011718668 +1.722625 -0.356292724609375 -0.490081787109375 0.2729187011718668 +1.72275 -0.356292724609375 -0.490081787109375 0.2729187011718668 +1.722875 -0.35552978515625 -0.489013671875 0.2729187011718668 +1.723 -0.354705810546875 -0.487884521484375 0.2729187011718668 +1.723125 -0.354705810546875 -0.487884521484375 0.2729187011718668 +1.72325 -0.353851318359375 -0.486724853515625 0.2729187011718668 +1.723375 -0.353851318359375 -0.486724853515625 0.2729187011718668 +1.7235 -0.35296630859375 -0.485504150390625 0.2729187011718668 +1.723625 -0.35205078125 -0.484222412109375 0.2729187011718668 +1.7237500000000002 -0.35205078125 -0.484222412109375 0.2729187011718668 +1.723875 -0.35107421875 -0.48291015625 0.2729187011718668 +1.724 -0.35107421875 -0.48291015625 0.2729187011718668 +1.7241250000000002 -0.350067138671875 -0.48150634765625 0.2729187011718668 +1.72425 -0.349029541015625 -0.480072021484375 0.2729187011718668 +1.724375 -0.349029541015625 -0.480072021484375 0.2729187011718668 +1.7245000000000002 -0.34796142578125 -0.478607177734375 0.2729187011718668 +1.724625 -0.34796142578125 -0.478607177734375 0.2729187011718668 +1.72475 -0.346832275390625 -0.47705078125 0.2729187011718668 +1.724875 -0.345672607421875 -0.4754638671875 0.2729187011718668 +1.725 -0.345672607421875 -0.4754638671875 0.2729187011718668 +1.725125 -0.344482421875 -0.47381591796875 0.2729187011718668 +1.72525 -0.344482421875 -0.47381591796875 0.2729187011718668 +1.725375 -0.343231201171875 -0.472137451171875 0.2729187011718668 +1.7255 -0.34197998046875 -0.470367431640625 0.2729187011718668 +1.725625 -0.34197998046875 -0.470367431640625 0.2729187011718668 +1.7257500000000002 -0.340667724609375 -0.46856689453125 0.2729187011718668 +1.725875 -0.340667724609375 -0.46856689453125 0.2729187011718668 +1.726 -0.339324951171875 -0.46673583984375 0.2729187011718668 +1.7261250000000002 -0.337921142578125 -0.464813232421875 0.2729187011718668 +1.72625 -0.337921142578125 -0.464813232421875 0.2729187011718668 +1.726375 -0.336517333984375 -0.462890625 0.2729187011718668 +1.7265000000000002 -0.336517333984375 -0.462890625 0.2729187011718668 +1.726625 -0.335052490234375 -0.46087646484375 0.2729187011718668 +1.72675 -0.333587646484375 -0.458831787109375 0.2729187011718668 +1.726875 -0.333587646484375 -0.458831787109375 0.2729187011718668 +1.727 -0.33203125 -0.45672607421875 0.2729187011718668 +1.727125 -0.33203125 -0.45672607421875 0.2729187011718668 +1.72725 -0.330474853515625 -0.454559326171875 0.2729187011718668 +1.727375 -0.328857421875 -0.452362060546875 0.2729187011718668 +1.7275 -0.328857421875 -0.452362060546875 0.2729187011718668 +1.727625 -0.327239990234375 -0.450103759765625 0.2729187011718668 +1.7277500000000002 -0.327239990234375 -0.450103759765625 0.2729187011718668 +1.727875 -0.3255615234375 -0.447784423828125 0.2729187011718668 +1.728 -0.4454345703125 -0.389617919921875 -0.12530517578125826 +1.7281250000000002 -0.4454345703125 -0.389617919921875 -0.12530517578125826 +1.72825 -0.44305419921875 -0.38751220703125 -0.12530517578125826 +1.728375 -0.44305419921875 -0.38751220703125 -0.12530517578125826 +1.7285 -0.440582275390625 -0.385345458984375 -0.12530517578125826 +1.728625 -0.438079833984375 -0.3831787109375 -0.12530517578125826 +1.72875 -0.438079833984375 -0.3831787109375 -0.12530517578125826 +1.728875 -0.435546875 -0.380950927734375 -0.12530517578125826 +1.729 -0.435546875 -0.380950927734375 -0.12530517578125826 +1.729125 -0.432952880859375 -0.378692626953125 -0.12530517578125826 +1.72925 -0.4302978515625 -0.376373291015625 -0.12530517578125826 +1.729375 -0.4302978515625 -0.376373291015625 -0.12530517578125826 +1.7295 -0.4276123046875 -0.3740234375 -0.12530517578125826 +1.729625 -0.4276123046875 -0.3740234375 -0.12530517578125826 +1.7297500000000002 -0.424896240234375 -0.371612548828125 -0.12530517578125826 +1.729875 -0.422119140625 -0.36920166015625 -0.12530517578125826 +1.73 -0.422119140625 -0.36920166015625 -0.12530517578125826 +1.7301250000000002 -0.419281005859375 -0.366729736328125 -0.12530517578125826 +1.73025 -0.419281005859375 -0.366729736328125 -0.12530517578125826 +1.730375 -0.416412353515625 -0.36419677734375 -0.12530517578125826 +1.7305 -0.413482666015625 -0.36163330078125 -0.12530517578125826 +1.730625 -0.413482666015625 -0.36163330078125 -0.12530517578125826 +1.73075 -0.4105224609375 -0.35906982421875 -0.12530517578125826 +1.730875 -0.4105224609375 -0.35906982421875 -0.12530517578125826 +1.731 -0.407501220703125 -0.356414794921875 -0.12530517578125826 +1.731125 -0.404449462890625 -0.353759765625 -0.12530517578125826 +1.73125 -0.404449462890625 -0.353759765625 -0.12530517578125826 +1.731375 -0.401336669921875 -0.351043701171875 -0.12530517578125826 +1.7315 -0.401336669921875 -0.351043701171875 -0.12530517578125826 +1.731625 -0.398193359375 -0.348297119140625 -0.12530517578125826 +1.7317500000000002 -0.39501953125 -0.34552001953125 -0.12530517578125826 +1.731875 -0.39501953125 -0.34552001953125 -0.12530517578125826 +1.732 -0.39178466796875 -0.342681884765625 -0.12530517578125826 +1.7321250000000002 -0.39178466796875 -0.342681884765625 -0.12530517578125826 +1.73225 -0.388519287109375 -0.339813232421875 -0.12530517578125826 +1.732375 -0.38519287109375 -0.3369140625 -0.12530517578125826 +1.7325 -0.38519287109375 -0.3369140625 -0.12530517578125826 +1.732625 -0.3818359375 -0.333984375 -0.12530517578125826 +1.73275 -0.3818359375 -0.333984375 -0.12530517578125826 +1.732875 -0.378448486328125 -0.33099365234375 -0.12530517578125826 +1.733 -0.375 -0.327972412109375 -0.12530517578125826 +1.733125 -0.375 -0.327972412109375 -0.12530517578125826 +1.73325 -0.37152099609375 -0.324951171875 -0.12530517578125826 +1.733375 -0.37152099609375 -0.324951171875 -0.12530517578125826 +1.7335 -0.36798095703125 -0.321868896484375 -0.12530517578125826 +1.733625 -0.36444091796875 -0.318756103515625 -0.12530517578125826 +1.7337500000000002 -0.36444091796875 -0.318756103515625 -0.12530517578125826 +1.733875 -0.360809326171875 -0.315582275390625 -0.12530517578125826 +1.734 -0.360809326171875 -0.315582275390625 -0.12530517578125826 +1.7341250000000002 -0.357177734375 -0.312408447265625 -0.12530517578125826 +1.73425 -0.353485107421875 -0.309173583984375 -0.12530517578125826 +1.734375 -0.353485107421875 -0.309173583984375 -0.12530517578125826 +1.7345 -0.349761962890625 -0.305938720703125 -0.12530517578125826 +1.734625 -0.349761962890625 -0.305938720703125 -0.12530517578125826 +1.73475 -0.34600830078125 -0.302642822265625 -0.12530517578125826 +1.734875 -0.34222412109375 -0.29931640625 -0.12530517578125826 +1.735 -0.34222412109375 -0.29931640625 -0.12530517578125826 +1.735125 -0.33837890625 -0.29595947265625 -0.12530517578125826 +1.73525 -0.33837890625 -0.29595947265625 -0.12530517578125826 +1.7353750000000002 -0.334503173828125 -0.292572021484375 -0.12530517578125826 +1.7355 -0.330596923828125 -0.289154052734375 -0.12530517578125826 +1.735625 -0.330596923828125 -0.289154052734375 -0.12530517578125826 +1.7357500000000002 -0.32666015625 -0.28570556640625 -0.12530517578125826 +1.735875 -0.32666015625 -0.28570556640625 -0.12530517578125826 +1.736 -0.322662353515625 -0.2822265625 -0.12530517578125826 +1.7361250000000002 -0.31866455078125 -0.278717041015625 -0.12530517578125826 +1.73625 -0.31866455078125 -0.278717041015625 -0.12530517578125826 +1.736375 -0.314605712890625 -0.275177001953125 -0.12530517578125826 +1.7365 -0.314605712890625 -0.275177001953125 -0.12530517578125826 +1.736625 -0.310516357421875 -0.2716064453125 -0.12530517578125826 +1.73675 -0.306396484375 -0.26800537109375 -0.12530517578125826 +1.736875 -0.306396484375 -0.26800537109375 -0.12530517578125826 +1.737 -0.30224609375 -0.26434326171875 -0.12530517578125826 +1.737125 -0.30224609375 -0.26434326171875 -0.12530517578125826 +1.73725 -0.298065185546875 -0.260711669921875 -0.12530517578125826 +1.7373750000000002 -0.2938232421875 -0.256988525390625 -0.12530517578125826 +1.7375 -0.2938232421875 -0.256988525390625 -0.12530517578125826 +1.737625 -0.289581298828125 -0.2532958984375 -0.12530517578125826 +1.7377500000000002 -0.289581298828125 -0.2532958984375 -0.12530517578125826 +1.737875 -0.285308837890625 -0.249542236328125 -0.12530517578125826 +1.738 -0.280975341796875 -0.245758056640625 -0.12530517578125826 +1.7381250000000002 -0.280975341796875 -0.245758056640625 -0.12530517578125826 +1.73825 -0.276641845703125 -0.241973876953125 -0.12530517578125826 +1.738375 -0.276641845703125 -0.241973876953125 -0.12530517578125826 +1.7385 -0.27227783203125 -0.2381591796875 -0.12530517578125826 +1.738625 -0.267852783203125 -0.234283447265625 -0.12530517578125826 +1.73875 -0.267852783203125 -0.234283447265625 -0.12530517578125826 +1.738875 -0.263427734375 -0.23040771484375 -0.12530517578125826 +1.739 -0.263427734375 -0.23040771484375 -0.12530517578125826 +1.739125 -0.25897216796875 -0.22650146484375 -0.12530517578125826 +1.73925 -0.25445556640625 -0.222564697265625 -0.12530517578125826 +1.7393750000000002 -0.25445556640625 -0.222564697265625 -0.12530517578125826 +1.7395 -0.249969482421875 -0.2186279296875 -0.12530517578125826 +1.739625 -0.249969482421875 -0.2186279296875 -0.12530517578125826 +1.7397500000000002 -0.24542236328125 -0.214630126953125 -0.12530517578125826 +1.739875 -0.2408447265625 -0.21063232421875 -0.12530517578125826 +1.74 -0.2408447265625 -0.21063232421875 -0.12530517578125826 +1.7401250000000002 -0.236236572265625 -0.206634521484375 -0.12530517578125826 +1.74025 -0.236236572265625 -0.206634521484375 -0.12530517578125826 +1.740375 -0.231597900390625 -0.20257568359375 -0.12530517578125826 +1.7405 -0.226959228515625 -0.198486328125 -0.12530517578125826 +1.740625 -0.226959228515625 -0.198486328125 -0.12530517578125826 +1.74075 -0.2222900390625 -0.194427490234375 -0.12530517578125826 +1.740875 -0.2222900390625 -0.194427490234375 -0.12530517578125826 +1.741 -0.21759033203125 -0.1903076171875 -0.12530517578125826 +1.741125 -0.212860107421875 -0.1861572265625 -0.12530517578125826 +1.74125 -0.212860107421875 -0.1861572265625 -0.12530517578125826 +1.7413750000000002 -0.208099365234375 -0.1820068359375 -0.12530517578125826 +1.7415 -0.208099365234375 -0.1820068359375 -0.12530517578125826 +1.741625 -0.203338623046875 -0.177825927734375 -0.12530517578125826 +1.7417500000000002 -0.19854736328125 -0.17364501953125 -0.12530517578125826 +1.741875 -0.19854736328125 -0.17364501953125 -0.12530517578125826 +1.742 -0.1937255859375 -0.16943359375 -0.12530517578125826 +1.7421250000000002 -0.1937255859375 -0.16943359375 -0.12530517578125826 +1.74225 -0.188873291015625 -0.165191650390625 -0.12530517578125826 +1.742375 -0.18402099609375 -0.16094970703125 -0.12530517578125826 +1.7425 -0.18402099609375 -0.16094970703125 -0.12530517578125826 +1.742625 -0.17913818359375 -0.15667724609375 -0.12530517578125826 +1.74275 -0.17913818359375 -0.15667724609375 -0.12530517578125826 +1.742875 -0.17425537109375 -0.15240478515625 -0.12530517578125826 +1.743 -0.169342041015625 -0.148101806640625 -0.12530517578125826 +1.743125 -0.169342041015625 -0.148101806640625 -0.12530517578125826 +1.74325 -0.164398193359375 -0.143768310546875 -0.12530517578125826 +1.7433750000000002 -0.164398193359375 -0.143768310546875 -0.12530517578125826 +1.7435 -0.159454345703125 -0.13946533203125 -0.12530517578125826 +1.743625 -0.15447998046875 -0.135101318359375 -0.12530517578125826 +1.7437500000000002 -0.15447998046875 -0.135101318359375 -0.12530517578125826 +1.743875 -0.14947509765625 -0.1307373046875 -0.12530517578125826 +1.744 -0.14947509765625 -0.1307373046875 -0.12530517578125826 +1.744125 -0.14447021484375 -0.126373291015625 -0.12530517578125826 +1.74425 -0.13946533203125 -0.121978759765625 -0.12530517578125826 +1.744375 -0.13946533203125 -0.121978759765625 -0.12530517578125826 +1.7445 -0.134429931640625 -0.117584228515625 -0.12530517578125826 +1.744625 -0.134429931640625 -0.117584228515625 -0.12530517578125826 +1.74475 -0.129364013671875 -0.1131591796875 -0.12530517578125826 +1.744875 -0.124298095703125 -0.108734130859375 -0.12530517578125826 +1.745 -0.124298095703125 -0.108734130859375 -0.12530517578125826 +1.745125 -0.119232177734375 -0.104278564453125 -0.12530517578125826 +1.74525 -0.119232177734375 -0.104278564453125 -0.12530517578125826 +1.7453750000000002 -0.1141357421875 -0.099822998046875 -0.12530517578125826 +1.7455 -0.109039306640625 -0.095367431640625 -0.12530517578125826 +1.745625 -0.109039306640625 -0.095367431640625 -0.12530517578125826 +1.7457500000000002 -0.103912353515625 -0.09088134765625 -0.12530517578125826 +1.745875 -0.103912353515625 -0.09088134765625 -0.12530517578125826 +1.746 -0.098785400390625 -0.086395263671875 -0.12530517578125826 +1.746125 -0.093658447265625 -0.0819091796875 -0.12530517578125826 +1.74625 -0.093658447265625 -0.0819091796875 -0.12530517578125826 +1.746375 -0.0885009765625 -0.077423095703125 -0.12530517578125826 +1.7465 -0.0885009765625 -0.077423095703125 -0.12530517578125826 +1.746625 -0.083343505859375 -0.072906494140625 -0.12530517578125826 +1.74675 -0.07818603515625 -0.068389892578125 -0.12530517578125826 +1.746875 -0.07818603515625 -0.068389892578125 -0.12530517578125826 +1.747 -0.072998046875 -0.0638427734375 -0.12530517578125826 +1.747125 -0.072998046875 -0.0638427734375 -0.12530517578125826 +1.74725 -0.06781005859375 -0.059326171875 -0.12530517578125826 +1.7473750000000002 -0.0626220703125 -0.054779052734375 -0.12530517578125826 +1.7475 -0.0626220703125 -0.054779052734375 -0.12530517578125826 +1.747625 -0.05743408203125 -0.05023193359375 -0.12530517578125826 +1.7477500000000002 -0.05743408203125 -0.05023193359375 -0.12530517578125826 +1.747875 -0.05224609375 -0.045684814453125 -0.12530517578125826 +1.748 -0.047027587890625 -0.0411376953125 -0.12530517578125826 +1.748125 -0.047027587890625 -0.0411376953125 -0.12530517578125826 +1.74825 -0.04180908203125 -0.03656005859375 -0.12530517578125826 +1.748375 -0.04180908203125 -0.03656005859375 -0.12530517578125826 +1.7485 -0.036590576171875 -0.032012939453125 -0.12530517578125826 +1.748625 -0.0313720703125 -0.027435302734375 -0.12530517578125826 +1.74875 -0.0313720703125 -0.027435302734375 -0.12530517578125826 +1.748875 -0.026123046875 -0.022857666015625 -0.12530517578125826 +1.749 -0.026123046875 -0.022857666015625 -0.12530517578125826 +1.749125 -0.020904541015625 -0.018280029296875 -0.12530517578125826 +1.74925 -0.01568603515625 -0.013702392578125 -0.12530517578125826 +1.7493750000000002 -0.01568603515625 -0.013702392578125 -0.12530517578125826 +1.7495 -0.01043701171875 -0.009124755859375 -0.12530517578125826 +1.749625 -0.01043701171875 -0.009124755859375 -0.12530517578125826 +1.7497500000000002 -0.005218505859375 -0.004547119140625 -0.12530517578125826 +1.749875 0.0 0.0 -0.12530517578125826 +1.75 0.0 0.0 -0.12530517578125826 +1.750125 0.005218505859375 0.004547119140625 -0.12530517578125826 +1.75025 0.005218505859375 0.004547119140625 -0.12530517578125826 +1.750375 0.01043701171875 0.009124755859375 -0.12530517578125826 +1.7505 0.01568603515625 0.013702392578125 -0.12530517578125826 +1.750625 0.01568603515625 0.013702392578125 -0.12530517578125826 +1.75075 0.020904541015625 0.018280029296875 -0.12530517578125826 +1.750875 0.020904541015625 0.018280029296875 -0.12530517578125826 +1.7510000000000002 0.026123046875 0.022857666015625 -0.12530517578125826 +1.751125 0.0313720703125 0.027435302734375 -0.12530517578125826 +1.75125 0.0313720703125 0.027435302734375 -0.12530517578125826 +1.7513750000000002 0.036590576171875 0.032012939453125 -0.12530517578125826 +1.7515 0.036590576171875 0.032012939453125 -0.12530517578125826 +1.751625 0.04180908203125 0.03656005859375 -0.12530517578125826 +1.7517500000000002 0.047027587890625 0.0411376953125 -0.12530517578125826 +1.751875 0.047027587890625 0.0411376953125 -0.12530517578125826 +1.752 0.05224609375 0.045684814453125 -0.12530517578125826 +1.752125 0.05224609375 0.045684814453125 -0.12530517578125826 +1.75225 0.05743408203125 0.05023193359375 -0.12530517578125826 +1.752375 0.0626220703125 0.054779052734375 -0.12530517578125826 +1.7525 0.0626220703125 0.054779052734375 -0.12530517578125826 +1.752625 0.06781005859375 0.059326171875 -0.12530517578125826 +1.75275 0.06781005859375 0.059326171875 -0.12530517578125826 +1.752875 0.072998046875 0.0638427734375 -0.12530517578125826 +1.7530000000000002 0.07818603515625 0.068389892578125 -0.12530517578125826 +1.753125 0.07818603515625 0.068389892578125 -0.12530517578125826 +1.75325 0.083343505859375 0.072906494140625 -0.12530517578125826 +1.7533750000000002 0.083343505859375 0.072906494140625 -0.12530517578125826 +1.7535 0.0885009765625 0.077423095703125 -0.12530517578125826 +1.753625 0.093658447265625 0.0819091796875 -0.12530517578125826 +1.7537500000000002 0.093658447265625 0.0819091796875 -0.12530517578125826 +1.753875 0.098785400390625 0.086395263671875 -0.12530517578125826 +1.754 0.098785400390625 0.086395263671875 -0.12530517578125826 +1.754125 0.103912353515625 0.09088134765625 -0.12530517578125826 +1.75425 0.109039306640625 0.095367431640625 -0.12530517578125826 +1.754375 0.109039306640625 0.095367431640625 -0.12530517578125826 +1.7545 0.1141357421875 0.099822998046875 -0.12530517578125826 +1.754625 0.1141357421875 0.099822998046875 -0.12530517578125826 +1.75475 0.119232177734375 0.104278564453125 -0.12530517578125826 +1.754875 0.124298095703125 0.108734130859375 -0.12530517578125826 +1.7550000000000002 0.124298095703125 0.108734130859375 -0.12530517578125826 +1.755125 0.129364013671875 0.1131591796875 -0.12530517578125826 +1.75525 0.129364013671875 0.1131591796875 -0.12530517578125826 +1.7553750000000002 0.134429931640625 0.117584228515625 -0.12530517578125826 +1.7555 0.13946533203125 0.121978759765625 -0.12530517578125826 +1.755625 0.13946533203125 0.121978759765625 -0.12530517578125826 +1.7557500000000002 0.14447021484375 0.126373291015625 -0.12530517578125826 +1.755875 0.14447021484375 0.126373291015625 -0.12530517578125826 +1.756 0.14947509765625 0.1307373046875 -0.12530517578125826 +1.756125 0.15447998046875 0.135101318359375 -0.12530517578125826 +1.75625 0.15447998046875 0.135101318359375 -0.12530517578125826 +1.756375 0.159454345703125 0.13946533203125 -0.12530517578125826 +1.7565 0.159454345703125 0.13946533203125 -0.12530517578125826 +1.756625 0.164398193359375 0.143768310546875 -0.12530517578125826 +1.75675 0.169342041015625 0.148101806640625 -0.12530517578125826 +1.756875 0.169342041015625 0.148101806640625 -0.12530517578125826 +1.7570000000000002 0.17425537109375 0.15240478515625 -0.12530517578125826 +1.757125 0.17425537109375 0.15240478515625 -0.12530517578125826 +1.75725 0.17913818359375 0.15667724609375 -0.12530517578125826 +1.7573750000000002 0.18402099609375 0.16094970703125 -0.12530517578125826 +1.7575 0.18402099609375 0.16094970703125 -0.12530517578125826 +1.757625 0.188873291015625 0.165191650390625 -0.12530517578125826 +1.7577500000000002 0.188873291015625 0.165191650390625 -0.12530517578125826 +1.757875 0.1937255859375 0.16943359375 -0.12530517578125826 +1.758 0.19854736328125 0.17364501953125 -0.12530517578125826 +1.758125 0.19854736328125 0.17364501953125 -0.12530517578125826 +1.75825 0.203338623046875 0.177825927734375 -0.12530517578125826 +1.758375 0.203338623046875 0.177825927734375 -0.12530517578125826 +1.7585 0.208099365234375 0.1820068359375 -0.12530517578125826 +1.758625 0.212860107421875 0.1861572265625 -0.12530517578125826 +1.75875 0.212860107421875 0.1861572265625 -0.12530517578125826 +1.758875 0.21759033203125 0.1903076171875 -0.12530517578125826 +1.7590000000000002 0.21759033203125 0.1903076171875 -0.12530517578125826 +1.759125 0.2222900390625 0.194427490234375 -0.12530517578125826 +1.75925 0.226959228515625 0.198486328125 -0.12530517578125826 +1.7593750000000002 0.226959228515625 0.198486328125 -0.12530517578125826 +1.7595 0.231597900390625 0.20257568359375 -0.12530517578125826 +1.759625 0.231597900390625 0.20257568359375 -0.12530517578125826 +1.7597500000000002 0.236236572265625 0.206634521484375 -0.12530517578125826 +1.759875 0.2408447265625 0.21063232421875 -0.12530517578125826 +1.76 0.2408447265625 0.119537353515625 -0.5035827636718825 +1.760125 0.24542236328125 0.121795654296875 -0.5035827636718825 +1.76025 0.24542236328125 0.121795654296875 -0.5035827636718825 +1.760375 0.249969482421875 0.124053955078125 -0.5035827636718825 +1.7605 0.25445556640625 0.126312255859375 -0.5035827636718825 +1.760625 0.25445556640625 0.126312255859375 -0.5035827636718825 +1.76075 0.25897216796875 0.1285400390625 -0.5035827636718825 +1.760875 0.25897216796875 0.1285400390625 -0.5035827636718825 +1.7610000000000002 0.263427734375 0.1307373046875 -0.5035827636718825 +1.761125 0.267852783203125 0.132965087890625 -0.5035827636718825 +1.76125 0.267852783203125 0.132965087890625 -0.5035827636718825 +1.7613750000000002 0.27227783203125 0.1351318359375 -0.5035827636718825 +1.7615 0.27227783203125 0.1351318359375 -0.5035827636718825 +1.761625 0.276641845703125 0.137298583984375 -0.5035827636718825 +1.76175 0.280975341796875 0.13946533203125 -0.5035827636718825 +1.761875 0.280975341796875 0.13946533203125 -0.5035827636718825 +1.762 0.285308837890625 0.1416015625 -0.5035827636718825 +1.762125 0.285308837890625 0.1416015625 -0.5035827636718825 +1.76225 0.289581298828125 0.14373779296875 -0.5035827636718825 +1.762375 0.2938232421875 0.145843505859375 -0.5035827636718825 +1.7625 0.2938232421875 0.145843505859375 -0.5035827636718825 +1.762625 0.298065185546875 0.14794921875 -0.5035827636718825 +1.76275 0.298065185546875 0.14794921875 -0.5035827636718825 +1.762875 0.30224609375 0.1500244140625 -0.5035827636718825 +1.7630000000000002 0.306396484375 0.152069091796875 -0.5035827636718825 +1.763125 0.306396484375 0.152069091796875 -0.5035827636718825 +1.76325 0.310516357421875 0.15411376953125 -0.5035827636718825 +1.7633750000000002 0.310516357421875 0.15411376953125 -0.5035827636718825 +1.7635 0.314605712890625 0.156158447265625 -0.5035827636718825 +1.763625 0.31866455078125 0.158172607421875 -0.5035827636718825 +1.76375 0.31866455078125 0.158172607421875 -0.5035827636718825 +1.763875 0.322662353515625 0.16015625 -0.5035827636718825 +1.764 0.322662353515625 0.16015625 -0.5035827636718825 +1.764125 0.32666015625 0.162139892578125 -0.5035827636718825 +1.76425 0.330596923828125 0.164093017578125 -0.5035827636718825 +1.764375 0.330596923828125 0.164093017578125 -0.5035827636718825 +1.7645 0.334503173828125 0.166046142578125 -0.5035827636718825 +1.764625 0.334503173828125 0.166046142578125 -0.5035827636718825 +1.76475 0.33837890625 0.16796875 -0.5035827636718825 +1.764875 0.34222412109375 0.16986083984375 -0.5035827636718825 +1.7650000000000002 0.34222412109375 0.16986083984375 -0.5035827636718825 +1.765125 0.34600830078125 0.1717529296875 -0.5035827636718825 +1.76525 0.34600830078125 0.1717529296875 -0.5035827636718825 +1.7653750000000002 0.349761962890625 0.173614501953125 -0.5035827636718825 +1.7655 0.353485107421875 0.175445556640625 -0.5035827636718825 +1.765625 0.353485107421875 0.175445556640625 -0.5035827636718825 +1.76575 0.357177734375 0.177276611328125 -0.5035827636718825 +1.765875 0.357177734375 0.177276611328125 -0.5035827636718825 +1.766 0.360809326171875 0.1790771484375 -0.5035827636718825 +1.766125 0.36444091796875 0.180877685546875 -0.5035827636718825 +1.76625 0.36444091796875 0.180877685546875 -0.5035827636718825 +1.766375 0.36798095703125 0.182647705078125 -0.5035827636718825 +1.7665 0.36798095703125 0.182647705078125 -0.5035827636718825 +1.7666250000000002 0.37152099609375 0.18438720703125 -0.5035827636718825 +1.76675 0.375 0.186126708984375 -0.5035827636718825 +1.766875 0.375 0.186126708984375 -0.5035827636718825 +1.7670000000000002 0.378448486328125 0.187835693359375 -0.5035827636718825 +1.767125 0.378448486328125 0.187835693359375 -0.5035827636718825 +1.76725 0.3818359375 0.18951416015625 -0.5035827636718825 +1.7673750000000002 0.38519287109375 0.191192626953125 -0.5035827636718825 +1.7675 0.38519287109375 0.191192626953125 -0.5035827636718825 +1.767625 0.388519287109375 0.192840576171875 -0.5035827636718825 +1.76775 0.388519287109375 0.192840576171875 -0.5035827636718825 +1.767875 0.39178466796875 0.1944580078125 -0.5035827636718825 +1.768 0.39501953125 0.196075439453125 -0.5035827636718825 +1.768125 0.39501953125 0.196075439453125 -0.5035827636718825 +1.76825 0.398193359375 0.1976318359375 -0.5035827636718825 +1.768375 0.398193359375 0.1976318359375 -0.5035827636718825 +1.7685 0.401336669921875 0.19921875 -0.5035827636718825 +1.7686250000000002 0.404449462890625 0.20074462890625 -0.5035827636718825 +1.76875 0.404449462890625 0.20074462890625 -0.5035827636718825 +1.768875 0.407501220703125 0.2022705078125 -0.5035827636718825 +1.7690000000000002 0.407501220703125 0.2022705078125 -0.5035827636718825 +1.769125 0.4105224609375 0.203765869140625 -0.5035827636718825 +1.76925 0.413482666015625 0.205230712890625 -0.5035827636718825 +1.7693750000000002 0.413482666015625 0.205230712890625 -0.5035827636718825 +1.7695 0.416412353515625 0.206695556640625 -0.5035827636718825 +1.769625 0.416412353515625 0.206695556640625 -0.5035827636718825 +1.76975 0.419281005859375 0.208099365234375 -0.5035827636718825 +1.769875 0.422119140625 0.209503173828125 -0.5035827636718825 +1.77 0.422119140625 0.209503173828125 -0.5035827636718825 +1.770125 0.424896240234375 0.210906982421875 -0.5035827636718825 +1.77025 0.424896240234375 0.210906982421875 -0.5035827636718825 +1.770375 0.4276123046875 0.212249755859375 -0.5035827636718825 +1.7705 0.4302978515625 0.213592529296875 -0.5035827636718825 +1.7706250000000002 0.4302978515625 0.213592529296875 -0.5035827636718825 +1.77075 0.432952880859375 0.21490478515625 -0.5035827636718825 +1.770875 0.432952880859375 0.21490478515625 -0.5035827636718825 +1.7710000000000002 0.435546875 0.2161865234375 -0.5035827636718825 +1.771125 0.438079833984375 0.217437744140625 -0.5035827636718825 +1.77125 0.438079833984375 0.217437744140625 -0.5035827636718825 +1.7713750000000002 0.440582275390625 0.21868896484375 -0.5035827636718825 +1.7715 0.440582275390625 0.21868896484375 -0.5035827636718825 +1.771625 0.44305419921875 0.21990966796875 -0.5035827636718825 +1.77175 0.4454345703125 0.221099853515625 -0.5035827636718825 +1.771875 0.4454345703125 0.221099853515625 -0.5035827636718825 +1.772 0.447784423828125 0.222259521484375 -0.5035827636718825 +1.772125 0.447784423828125 0.222259521484375 -0.5035827636718825 +1.77225 0.450103759765625 0.223388671875 -0.5035827636718825 +1.772375 0.452362060546875 0.224517822265625 -0.5035827636718825 +1.7725 0.452362060546875 0.224517822265625 -0.5035827636718825 +1.7726250000000002 0.454559326171875 0.225616455078125 -0.5035827636718825 +1.77275 0.454559326171875 0.225616455078125 -0.5035827636718825 +1.772875 0.45672607421875 0.2266845703125 -0.5035827636718825 +1.7730000000000002 0.458831787109375 0.22772216796875 -0.5035827636718825 +1.773125 0.458831787109375 0.22772216796875 -0.5035827636718825 +1.77325 0.46087646484375 0.228759765625 -0.5035827636718825 +1.7733750000000002 0.46087646484375 0.228759765625 -0.5035827636718825 +1.7735 0.462890625 0.229736328125 -0.5035827636718825 +1.773625 0.464813232421875 0.230712890625 -0.5035827636718825 +1.77375 0.464813232421875 0.230712890625 -0.5035827636718825 +1.773875 0.46673583984375 0.231658935546875 -0.5035827636718825 +1.774 0.46673583984375 0.231658935546875 -0.5035827636718825 +1.774125 0.46856689453125 0.232574462890625 -0.5035827636718825 +1.77425 0.470367431640625 0.23345947265625 -0.5035827636718825 +1.774375 0.470367431640625 0.23345947265625 -0.5035827636718825 +1.7745 0.472137451171875 0.234344482421875 -0.5035827636718825 +1.7746250000000002 0.472137451171875 0.234344482421875 -0.5035827636718825 +1.77475 0.47381591796875 0.23516845703125 -0.5035827636718825 +1.774875 0.4754638671875 0.235992431640625 -0.5035827636718825 +1.7750000000000002 0.4754638671875 0.235992431640625 -0.5035827636718825 +1.775125 0.47705078125 0.236785888671875 -0.5035827636718825 +1.77525 0.47705078125 0.236785888671875 -0.5035827636718825 +1.7753750000000002 0.478607177734375 0.237548828125 -0.5035827636718825 +1.7755 0.480072021484375 0.23828125 -0.5035827636718825 +1.775625 0.480072021484375 0.23828125 -0.5035827636718825 +1.77575 0.48150634765625 0.238983154296875 -0.5035827636718825 +1.775875 0.48150634765625 0.238983154296875 -0.5035827636718825 +1.776 0.48291015625 0.23968505859375 -0.5035827636718825 +1.776125 0.484222412109375 0.2403564453125 -0.5035827636718825 +1.77625 0.484222412109375 0.2403564453125 -0.5035827636718825 +1.776375 0.485504150390625 0.240966796875 -0.5035827636718825 +1.7765 0.485504150390625 0.240966796875 -0.5035827636718825 +1.7766250000000002 0.486724853515625 0.2415771484375 -0.5035827636718825 +1.77675 0.487884521484375 0.242156982421875 -0.5035827636718825 +1.776875 0.487884521484375 0.242156982421875 -0.5035827636718825 +1.7770000000000002 0.489013671875 0.242706298828125 -0.5035827636718825 +1.777125 0.489013671875 0.242706298828125 -0.5035827636718825 +1.77725 0.490081787109375 0.243255615234375 -0.5035827636718825 +1.777375 0.4910888671875 0.243743896484375 -0.5035827636718825 +1.7775 0.4910888671875 0.243743896484375 -0.5035827636718825 +1.777625 0.492034912109375 0.244232177734375 -0.5035827636718825 +1.77775 0.492034912109375 0.244232177734375 -0.5035827636718825 +1.777875 0.492950439453125 0.244659423828125 -0.5035827636718825 +1.778 0.4937744140625 0.245086669921875 -0.5035827636718825 +1.778125 0.4937744140625 0.245086669921875 -0.5035827636718825 +1.77825 0.49456787109375 0.2454833984375 -0.5035827636718825 +1.778375 0.49456787109375 0.2454833984375 -0.5035827636718825 +1.7785 0.49530029296875 0.245849609375 -0.5035827636718825 +1.7786250000000002 0.496002197265625 0.246185302734375 -0.5035827636718825 +1.77875 0.496002197265625 0.246185302734375 -0.5035827636718825 +1.778875 0.496612548828125 0.246490478515625 -0.5035827636718825 +1.7790000000000002 0.496612548828125 0.246490478515625 -0.5035827636718825 +1.779125 0.4971923828125 0.246795654296875 -0.5035827636718825 +1.77925 0.497711181640625 0.247039794921875 -0.5035827636718825 +1.779375 0.497711181640625 0.247039794921875 -0.5035827636718825 +1.7795 0.498199462890625 0.247283935546875 -0.5035827636718825 +1.779625 0.498199462890625 0.247283935546875 -0.5035827636718825 +1.77975 0.49859619140625 0.247467041015625 -0.5035827636718825 +1.779875 0.49896240234375 0.247650146484375 -0.5035827636718825 +1.78 0.49896240234375 0.247650146484375 -0.5035827636718825 +1.780125 0.499267578125 0.247802734375 -0.5035827636718825 +1.78025 0.499267578125 0.247802734375 -0.5035827636718825 +1.780375 0.49951171875 0.2479248046875 -0.5035827636718825 +1.7805 0.49969482421875 0.248016357421875 -0.5035827636718825 +1.7806250000000002 0.49969482421875 0.248016357421875 -0.5035827636718825 +1.78075 0.49981689453125 0.248077392578125 -0.5035827636718825 +1.780875 0.49981689453125 0.248077392578125 -0.5035827636718825 +1.7810000000000002 0.499908447265625 0.248138427734375 -0.5035827636718825 +1.781125 0.49993896484375 0.248138427734375 -0.5035827636718825 +1.78125 0.49993896484375 0.248138427734375 -0.5035827636718825 +1.781375 0.499908447265625 0.248138427734375 -0.5035827636718825 +1.7815 0.499908447265625 0.248138427734375 -0.5035827636718825 +1.781625 0.49981689453125 0.248077392578125 -0.5035827636718825 +1.78175 0.49969482421875 0.248016357421875 -0.5035827636718825 +1.781875 0.49969482421875 0.248016357421875 -0.5035827636718825 +1.782 0.49951171875 0.2479248046875 -0.5035827636718825 +1.782125 0.49951171875 0.2479248046875 -0.5035827636718825 +1.7822500000000002 0.499267578125 0.247802734375 -0.5035827636718825 +1.782375 0.49896240234375 0.247650146484375 -0.5035827636718825 +1.7825 0.49896240234375 0.247650146484375 -0.5035827636718825 +1.7826250000000002 0.49859619140625 0.247467041015625 -0.5035827636718825 +1.78275 0.49859619140625 0.247467041015625 -0.5035827636718825 +1.782875 0.498199462890625 0.247283935546875 -0.5035827636718825 +1.7830000000000002 0.497711181640625 0.247039794921875 -0.5035827636718825 +1.783125 0.497711181640625 0.247039794921875 -0.5035827636718825 +1.78325 0.4971923828125 0.246795654296875 -0.5035827636718825 +1.783375 0.4971923828125 0.246795654296875 -0.5035827636718825 +1.7835 0.496612548828125 0.246490478515625 -0.5035827636718825 +1.783625 0.496002197265625 0.246185302734375 -0.5035827636718825 +1.78375 0.496002197265625 0.246185302734375 -0.5035827636718825 +1.783875 0.49530029296875 0.245849609375 -0.5035827636718825 +1.784 0.49530029296875 0.245849609375 -0.5035827636718825 +1.784125 0.49456787109375 0.2454833984375 -0.5035827636718825 +1.7842500000000002 0.4937744140625 0.245086669921875 -0.5035827636718825 +1.784375 0.4937744140625 0.245086669921875 -0.5035827636718825 +1.7845 0.492950439453125 0.244659423828125 -0.5035827636718825 +1.7846250000000002 0.492950439453125 0.244659423828125 -0.5035827636718825 +1.78475 0.492034912109375 0.244232177734375 -0.5035827636718825 +1.784875 0.4910888671875 0.243743896484375 -0.5035827636718825 +1.7850000000000002 0.4910888671875 0.243743896484375 -0.5035827636718825 +1.785125 0.490081787109375 0.243255615234375 -0.5035827636718825 +1.78525 0.490081787109375 0.243255615234375 -0.5035827636718825 +1.785375 0.489013671875 0.242706298828125 -0.5035827636718825 +1.7855 0.487884521484375 0.242156982421875 -0.5035827636718825 +1.785625 0.487884521484375 0.242156982421875 -0.5035827636718825 +1.78575 0.486724853515625 0.2415771484375 -0.5035827636718825 +1.785875 0.486724853515625 0.2415771484375 -0.5035827636718825 +1.786 0.485504150390625 0.240966796875 -0.5035827636718825 +1.786125 0.484222412109375 0.2403564453125 -0.5035827636718825 +1.7862500000000002 0.484222412109375 0.2403564453125 -0.5035827636718825 +1.786375 0.48291015625 0.23968505859375 -0.5035827636718825 +1.7865 0.48291015625 0.23968505859375 -0.5035827636718825 +1.7866250000000002 0.48150634765625 0.238983154296875 -0.5035827636718825 +1.78675 0.480072021484375 0.23828125 -0.5035827636718825 +1.786875 0.480072021484375 0.23828125 -0.5035827636718825 +1.7870000000000002 0.478607177734375 0.237548828125 -0.5035827636718825 +1.787125 0.478607177734375 0.237548828125 -0.5035827636718825 +1.78725 0.47705078125 0.236785888671875 -0.5035827636718825 +1.787375 0.4754638671875 0.235992431640625 -0.5035827636718825 +1.7875 0.4754638671875 0.235992431640625 -0.5035827636718825 +1.787625 0.47381591796875 0.23516845703125 -0.5035827636718825 +1.78775 0.47381591796875 0.23516845703125 -0.5035827636718825 +1.787875 0.472137451171875 0.234344482421875 -0.5035827636718825 +1.788 0.470367431640625 0.23345947265625 -0.5035827636718825 +1.788125 0.470367431640625 0.23345947265625 -0.5035827636718825 +1.7882500000000002 0.46856689453125 0.232574462890625 -0.5035827636718825 +1.788375 0.46856689453125 0.232574462890625 -0.5035827636718825 +1.7885 0.46673583984375 0.231658935546875 -0.5035827636718825 +1.7886250000000002 0.464813232421875 0.230712890625 -0.5035827636718825 +1.78875 0.464813232421875 0.230712890625 -0.5035827636718825 +1.788875 0.462890625 0.229736328125 -0.5035827636718825 +1.7890000000000002 0.462890625 0.229736328125 -0.5035827636718825 +1.789125 0.46087646484375 0.228759765625 -0.5035827636718825 +1.78925 0.458831787109375 0.22772216796875 -0.5035827636718825 +1.789375 0.458831787109375 0.22772216796875 -0.5035827636718825 +1.7895 0.45672607421875 0.2266845703125 -0.5035827636718825 +1.789625 0.45672607421875 0.2266845703125 -0.5035827636718825 +1.78975 0.454559326171875 0.225616455078125 -0.5035827636718825 +1.789875 0.452362060546875 0.224517822265625 -0.5035827636718825 +1.79 0.452362060546875 0.224517822265625 -0.5035827636718825 +1.790125 0.450103759765625 0.223388671875 -0.5035827636718825 +1.7902500000000002 0.450103759765625 0.223388671875 -0.5035827636718825 +1.790375 0.447784423828125 0.222259521484375 -0.5035827636718825 +1.7905 0.4454345703125 0.221099853515625 -0.5035827636718825 +1.7906250000000002 0.4454345703125 0.221099853515625 -0.5035827636718825 +1.79075 0.44305419921875 0.21990966796875 -0.5035827636718825 +1.790875 0.44305419921875 0.21990966796875 -0.5035827636718825 +1.7910000000000002 0.440582275390625 0.21868896484375 -0.5035827636718825 +1.791125 0.438079833984375 0.217437744140625 -0.5035827636718825 +1.79125 0.438079833984375 0.217437744140625 -0.5035827636718825 +1.791375 0.435546875 0.2161865234375 -0.5035827636718825 +1.7915 0.435546875 0.2161865234375 -0.5035827636718825 +1.791625 0.432952880859375 0.21490478515625 -0.5035827636718825 +1.79175 0.4302978515625 0.213592529296875 -0.5035827636718825 +1.791875 0.4302978515625 0.213592529296875 -0.5035827636718825 +1.792 0.4276123046875 0.0848388671875 -0.8015075683593804 +1.792125 0.4276123046875 0.0848388671875 -0.8015075683593804 +1.7922500000000002 0.424896240234375 0.08428955078125 -0.8015075683593804 +1.792375 0.422119140625 0.083740234375 -0.8015075683593804 +1.7925 0.422119140625 0.083740234375 -0.8015075683593804 +1.7926250000000002 0.419281005859375 0.083160400390625 -0.8015075683593804 +1.79275 0.419281005859375 0.083160400390625 -0.8015075683593804 +1.792875 0.416412353515625 0.082611083984375 -0.8015075683593804 +1.793 0.413482666015625 0.08203125 -0.8015075683593804 +1.793125 0.413482666015625 0.08203125 -0.8015075683593804 +1.79325 0.4105224609375 0.081451416015625 -0.8015075683593804 +1.793375 0.4105224609375 0.081451416015625 -0.8015075683593804 +1.7935 0.407501220703125 0.080841064453125 -0.8015075683593804 +1.793625 0.404449462890625 0.080230712890625 -0.8015075683593804 +1.79375 0.404449462890625 0.080230712890625 -0.8015075683593804 +1.793875 0.401336669921875 0.079620361328125 -0.8015075683593804 +1.794 0.401336669921875 0.079620361328125 -0.8015075683593804 +1.794125 0.398193359375 0.0789794921875 -0.8015075683593804 +1.7942500000000002 0.39501953125 0.078369140625 -0.8015075683593804 +1.794375 0.39501953125 0.078369140625 -0.8015075683593804 +1.7945 0.39178466796875 0.077728271484375 -0.8015075683593804 +1.7946250000000002 0.39178466796875 0.077728271484375 -0.8015075683593804 +1.79475 0.388519287109375 0.077056884765625 -0.8015075683593804 +1.794875 0.38519287109375 0.076416015625 -0.8015075683593804 +1.795 0.38519287109375 0.076416015625 -0.8015075683593804 +1.795125 0.3818359375 0.07574462890625 -0.8015075683593804 +1.79525 0.3818359375 0.07574462890625 -0.8015075683593804 +1.795375 0.378448486328125 0.0750732421875 -0.8015075683593804 +1.7955 0.375 0.07440185546875 -0.8015075683593804 +1.795625 0.375 0.07440185546875 -0.8015075683593804 +1.79575 0.37152099609375 0.073699951171875 -0.8015075683593804 +1.795875 0.37152099609375 0.073699951171875 -0.8015075683593804 +1.796 0.36798095703125 0.072998046875 -0.8015075683593804 +1.796125 0.36444091796875 0.072296142578125 -0.8015075683593804 +1.7962500000000002 0.36444091796875 0.072296142578125 -0.8015075683593804 +1.796375 0.360809326171875 0.071563720703125 -0.8015075683593804 +1.7965 0.360809326171875 0.071563720703125 -0.8015075683593804 +1.7966250000000002 0.357177734375 0.07086181640625 -0.8015075683593804 +1.79675 0.353485107421875 0.07012939453125 -0.8015075683593804 +1.796875 0.353485107421875 0.07012939453125 -0.8015075683593804 +1.797 0.349761962890625 0.06939697265625 -0.8015075683593804 +1.797125 0.349761962890625 0.06939697265625 -0.8015075683593804 +1.79725 0.34600830078125 0.068634033203125 -0.8015075683593804 +1.797375 0.34222412109375 0.06787109375 -0.8015075683593804 +1.7975 0.34222412109375 0.06787109375 -0.8015075683593804 +1.797625 0.33837890625 0.067138671875 -0.8015075683593804 +1.79775 0.33837890625 0.067138671875 -0.8015075683593804 +1.7978750000000002 0.334503173828125 0.06634521484375 -0.8015075683593804 +1.798 0.330596923828125 0.065582275390625 -0.8015075683593804 +1.798125 0.330596923828125 0.065582275390625 -0.8015075683593804 +1.7982500000000002 0.32666015625 0.064788818359375 -0.8015075683593804 +1.798375 0.32666015625 0.064788818359375 -0.8015075683593804 +1.7985 0.322662353515625 0.063995361328125 -0.8015075683593804 +1.7986250000000002 0.31866455078125 0.063201904296875 -0.8015075683593804 +1.79875 0.31866455078125 0.063201904296875 -0.8015075683593804 +1.798875 0.314605712890625 0.062408447265625 -0.8015075683593804 +1.799 0.314605712890625 0.062408447265625 -0.8015075683593804 +1.799125 0.310516357421875 0.06158447265625 -0.8015075683593804 +1.79925 0.306396484375 0.060791015625 -0.8015075683593804 +1.799375 0.306396484375 0.060791015625 -0.8015075683593804 +1.7995 0.30224609375 0.059967041015625 -0.8015075683593804 +1.799625 0.30224609375 0.059967041015625 -0.8015075683593804 +1.79975 0.298065185546875 0.059112548828125 -0.8015075683593804 +1.7998750000000002 0.2938232421875 0.05828857421875 -0.8015075683593804 +1.8 0.2938232421875 0.05828857421875 -0.8015075683593804 +1.800125 0.289581298828125 0.05743408203125 -0.8015075683593804 +1.8002500000000002 0.289581298828125 0.05743408203125 -0.8015075683593804 +1.800375 0.285308837890625 0.05657958984375 -0.8015075683593804 +1.8005 0.280975341796875 0.05572509765625 -0.8015075683593804 +1.8006250000000002 0.280975341796875 0.05572509765625 -0.8015075683593804 +1.80075 0.276641845703125 0.05487060546875 -0.8015075683593804 +1.800875 0.276641845703125 0.05487060546875 -0.8015075683593804 +1.801 0.27227783203125 0.05401611328125 -0.8015075683593804 +1.801125 0.267852783203125 0.053131103515625 -0.8015075683593804 +1.80125 0.267852783203125 0.053131103515625 -0.8015075683593804 +1.801375 0.263427734375 0.05224609375 -0.8015075683593804 +1.8015 0.263427734375 0.05224609375 -0.8015075683593804 +1.801625 0.25897216796875 0.051361083984375 -0.8015075683593804 +1.80175 0.25445556640625 0.05047607421875 -0.8015075683593804 +1.80187500000000016 0.25445556640625 0.05047607421875 -0.8015075683593804 +1.802 0.249969482421875 0.049591064453125 -0.8015075683593804 +1.802125 0.249969482421875 0.049591064453125 -0.8015075683593804 +1.80225000000000016 0.24542236328125 0.048675537109375 -0.8015075683593804 +1.802375 0.2408447265625 0.047760009765625 -0.8015075683593804 +1.8025 0.2408447265625 0.047760009765625 -0.8015075683593804 +1.80262500000000016 0.236236572265625 0.046844482421875 -0.8015075683593804 +1.80275 0.236236572265625 0.046844482421875 -0.8015075683593804 +1.802875 0.231597900390625 0.045928955078125 -0.8015075683593804 +1.803 0.226959228515625 0.045013427734375 -0.8015075683593804 +1.803125 0.226959228515625 0.045013427734375 -0.8015075683593804 +1.80325 0.2222900390625 0.044097900390625 -0.8015075683593804 +1.803375 0.2222900390625 0.044097900390625 -0.8015075683593804 +1.8035 0.21759033203125 0.04315185546875 -0.8015075683593804 +1.803625 0.212860107421875 0.042205810546875 -0.8015075683593804 +1.80375 0.212860107421875 0.042205810546875 -0.8015075683593804 +1.80387500000000016 0.208099365234375 0.041290283203125 -0.8015075683593804 +1.804 0.208099365234375 0.041290283203125 -0.8015075683593804 +1.804125 0.203338623046875 0.04034423828125 -0.8015075683593804 +1.80425000000000016 0.19854736328125 0.03936767578125 -0.8015075683593804 +1.804375 0.19854736328125 0.03936767578125 -0.8015075683593804 +1.8045 0.1937255859375 0.038421630859375 -0.8015075683593804 +1.804625000000000256 0.1937255859375 0.038421630859375 -0.8015075683593804 +1.80475 0.188873291015625 0.0374755859375 -0.8015075683593804 +1.804875 0.18402099609375 0.0364990234375 -0.8015075683593804 +1.805 0.18402099609375 0.0364990234375 -0.8015075683593804 +1.805125 0.17913818359375 0.0355224609375 -0.8015075683593804 +1.80525 0.17913818359375 0.0355224609375 -0.8015075683593804 +1.805375 0.17425537109375 0.0345458984375 -0.8015075683593804 +1.8055 0.169342041015625 0.0335693359375 -0.8015075683593804 +1.805625 0.169342041015625 0.0335693359375 -0.8015075683593804 +1.80575 0.164398193359375 0.0325927734375 -0.8015075683593804 +1.80587500000000016 0.164398193359375 0.0325927734375 -0.8015075683593804 +1.806 0.159454345703125 0.0316162109375 -0.8015075683593804 +1.806125 0.15447998046875 0.0306396484375 -0.8015075683593804 +1.80625000000000016 0.15447998046875 0.0306396484375 -0.8015075683593804 +1.806375 0.14947509765625 0.029632568359375 -0.8015075683593804 +1.8065 0.14947509765625 0.029632568359375 -0.8015075683593804 +1.806625000000000256 0.14447021484375 0.028656005859375 -0.8015075683593804 +1.80675 0.13946533203125 0.02764892578125 -0.8015075683593804 +1.806875 0.13946533203125 0.02764892578125 -0.8015075683593804 +1.807 0.134429931640625 0.02667236328125 -0.8015075683593804 +1.807125 0.134429931640625 0.02667236328125 -0.8015075683593804 +1.80725 0.129364013671875 0.025665283203125 -0.8015075683593804 +1.807375 0.124298095703125 0.024658203125 -0.8015075683593804 +1.8075 0.124298095703125 0.024658203125 -0.8015075683593804 +1.807625 0.119232177734375 0.023651123046875 -0.8015075683593804 +1.80775 0.119232177734375 0.023651123046875 -0.8015075683593804 +1.80787500000000016 0.1141357421875 0.02264404296875 -0.8015075683593804 +1.808 0.109039306640625 0.0216064453125 -0.8015075683593804 +1.808125 0.109039306640625 0.0216064453125 -0.8015075683593804 +1.80825000000000016 0.103912353515625 0.020599365234375 -0.8015075683593804 +1.808375 0.103912353515625 0.020599365234375 -0.8015075683593804 +1.8085 0.098785400390625 0.01959228515625 -0.8015075683593804 +1.808625 0.093658447265625 0.0185546875 -0.8015075683593804 +1.80875 0.093658447265625 0.0185546875 -0.8015075683593804 +1.808875 0.0885009765625 0.017547607421875 -0.8015075683593804 +1.809 0.0885009765625 0.017547607421875 -0.8015075683593804 +1.809125 0.083343505859375 0.01654052734375 -0.8015075683593804 +1.80925 0.07818603515625 0.0155029296875 -0.8015075683593804 +1.809375 0.07818603515625 0.0155029296875 -0.8015075683593804 +1.8095 0.072998046875 0.01446533203125 -0.8015075683593804 +1.809625 0.072998046875 0.01446533203125 -0.8015075683593804 +1.80975 0.06781005859375 0.013458251953125 -0.8015075683593804 +1.80987500000000016 0.0626220703125 0.012420654296875 -0.8015075683593804 +1.81 0.0626220703125 0.012420654296875 -0.8015075683593804 +1.810125 0.05743408203125 0.011383056640625 -0.8015075683593804 +1.81025000000000016 0.05743408203125 0.011383056640625 -0.8015075683593804 +1.810375 0.05224609375 0.010345458984375 -0.8015075683593804 +1.8105 0.047027587890625 0.009307861328125 -0.8015075683593804 +1.810625 0.047027587890625 0.009307861328125 -0.8015075683593804 +1.81075 0.04180908203125 0.008270263671875 -0.8015075683593804 +1.810875 0.04180908203125 0.008270263671875 -0.8015075683593804 +1.811 0.036590576171875 0.007232666015625 -0.8015075683593804 +1.811125 0.0313720703125 0.0062255859375 -0.8015075683593804 +1.81125 0.0313720703125 0.0062255859375 -0.8015075683593804 +1.811375 0.026123046875 0.00518798828125 -0.8015075683593804 +1.8115 0.026123046875 0.00518798828125 -0.8015075683593804 +1.811625 0.020904541015625 0.004150390625 -0.8015075683593804 +1.81175 0.01568603515625 0.00311279296875 -0.8015075683593804 +1.81187500000000016 0.01568603515625 0.00311279296875 -0.8015075683593804 +1.812 0.01043701171875 0.0020751953125 -0.8015075683593804 +1.812125 0.01043701171875 0.0020751953125 -0.8015075683593804 +1.81225000000000016 0.005218505859375 0.00103759765625 -0.8015075683593804 +1.812375 0.0 0.0 -0.8015075683593804 +1.8125 0.0 0.0 -0.8015075683593804 +1.812625 -0.005218505859375 -0.00103759765625 -0.8015075683593804 +1.81275 -0.005218505859375 -0.00103759765625 -0.8015075683593804 +1.812875 -0.01043701171875 -0.0020751953125 -0.8015075683593804 +1.813 -0.01568603515625 -0.00311279296875 -0.8015075683593804 +1.813125 -0.01568603515625 -0.00311279296875 -0.8015075683593804 +1.81325 -0.020904541015625 -0.004150390625 -0.8015075683593804 +1.813375 -0.020904541015625 -0.004150390625 -0.8015075683593804 +1.81350000000000016 -0.026123046875 -0.00518798828125 -0.8015075683593804 +1.813625 -0.0313720703125 -0.0062255859375 -0.8015075683593804 +1.81375 -0.0313720703125 -0.0062255859375 -0.8015075683593804 +1.81387500000000016 -0.036590576171875 -0.007232666015625 -0.8015075683593804 +1.814 -0.036590576171875 -0.007232666015625 -0.8015075683593804 +1.814125 -0.04180908203125 -0.008270263671875 -0.8015075683593804 +1.81425000000000016 -0.047027587890625 -0.009307861328125 -0.8015075683593804 +1.814375 -0.047027587890625 -0.009307861328125 -0.8015075683593804 +1.8145 -0.05224609375 -0.010345458984375 -0.8015075683593804 +1.814625 -0.05224609375 -0.010345458984375 -0.8015075683593804 +1.81475 -0.05743408203125 -0.011383056640625 -0.8015075683593804 +1.814875 -0.0626220703125 -0.012420654296875 -0.8015075683593804 +1.815 -0.0626220703125 -0.012420654296875 -0.8015075683593804 +1.815125 -0.06781005859375 -0.013458251953125 -0.8015075683593804 +1.81525 -0.06781005859375 -0.013458251953125 -0.8015075683593804 +1.815375 -0.072998046875 -0.01446533203125 -0.8015075683593804 +1.81550000000000016 -0.07818603515625 -0.0155029296875 -0.8015075683593804 +1.815625 -0.07818603515625 -0.0155029296875 -0.8015075683593804 +1.81575 -0.083343505859375 -0.01654052734375 -0.8015075683593804 +1.81587500000000016 -0.083343505859375 -0.01654052734375 -0.8015075683593804 +1.816 -0.0885009765625 -0.017547607421875 -0.8015075683593804 +1.816125 -0.093658447265625 -0.0185546875 -0.8015075683593804 +1.81625000000000016 -0.093658447265625 -0.0185546875 -0.8015075683593804 +1.816375 -0.098785400390625 -0.01959228515625 -0.8015075683593804 +1.8165 -0.098785400390625 -0.01959228515625 -0.8015075683593804 +1.816625 -0.103912353515625 -0.020599365234375 -0.8015075683593804 +1.81675 -0.109039306640625 -0.0216064453125 -0.8015075683593804 +1.816875 -0.109039306640625 -0.0216064453125 -0.8015075683593804 +1.817 -0.1141357421875 -0.02264404296875 -0.8015075683593804 +1.817125 -0.1141357421875 -0.02264404296875 -0.8015075683593804 +1.81725 -0.119232177734375 -0.023651123046875 -0.8015075683593804 +1.817375 -0.124298095703125 -0.024658203125 -0.8015075683593804 +1.81750000000000016 -0.124298095703125 -0.024658203125 -0.8015075683593804 +1.817625 -0.129364013671875 -0.025665283203125 -0.8015075683593804 +1.81775 -0.129364013671875 -0.025665283203125 -0.8015075683593804 +1.81787500000000016 -0.134429931640625 -0.02667236328125 -0.8015075683593804 +1.818 -0.13946533203125 -0.02764892578125 -0.8015075683593804 +1.818125 -0.13946533203125 -0.02764892578125 -0.8015075683593804 +1.81825000000000016 -0.14447021484375 -0.028656005859375 -0.8015075683593804 +1.818375 -0.14447021484375 -0.028656005859375 -0.8015075683593804 +1.8185 -0.14947509765625 -0.029632568359375 -0.8015075683593804 +1.818625 -0.15447998046875 -0.0306396484375 -0.8015075683593804 +1.81875 -0.15447998046875 -0.0306396484375 -0.8015075683593804 +1.818875 -0.159454345703125 -0.0316162109375 -0.8015075683593804 +1.819 -0.159454345703125 -0.0316162109375 -0.8015075683593804 +1.819125 -0.164398193359375 -0.0325927734375 -0.8015075683593804 +1.81925 -0.169342041015625 -0.0335693359375 -0.8015075683593804 +1.819375 -0.169342041015625 -0.0335693359375 -0.8015075683593804 +1.81950000000000016 -0.17425537109375 -0.0345458984375 -0.8015075683593804 +1.819625 -0.17425537109375 -0.0345458984375 -0.8015075683593804 +1.81975 -0.17913818359375 -0.0355224609375 -0.8015075683593804 +1.81987500000000016 -0.18402099609375 -0.0364990234375 -0.8015075683593804 +1.82 -0.18402099609375 -0.0364990234375 -0.8015075683593804 +1.820125 -0.188873291015625 -0.0374755859375 -0.8015075683593804 +1.820250000000000256 -0.188873291015625 -0.0374755859375 -0.8015075683593804 +1.820375 -0.1937255859375 -0.038421630859375 -0.8015075683593804 +1.8205 -0.19854736328125 -0.03936767578125 -0.8015075683593804 +1.820625 -0.19854736328125 -0.03936767578125 -0.8015075683593804 +1.82075 -0.203338623046875 -0.04034423828125 -0.8015075683593804 +1.820875 -0.203338623046875 -0.04034423828125 -0.8015075683593804 +1.821 -0.208099365234375 -0.041290283203125 -0.8015075683593804 +1.821125 -0.212860107421875 -0.042205810546875 -0.8015075683593804 +1.82125 -0.212860107421875 -0.042205810546875 -0.8015075683593804 +1.821375 -0.21759033203125 -0.04315185546875 -0.8015075683593804 +1.82150000000000016 -0.21759033203125 -0.04315185546875 -0.8015075683593804 +1.821625 -0.2222900390625 -0.044097900390625 -0.8015075683593804 +1.82175 -0.226959228515625 -0.045013427734375 -0.8015075683593804 +1.82187500000000016 -0.226959228515625 -0.045013427734375 -0.8015075683593804 +1.822 -0.231597900390625 -0.045928955078125 -0.8015075683593804 +1.822125 -0.231597900390625 -0.045928955078125 -0.8015075683593804 +1.822250000000000256 -0.236236572265625 -0.046844482421875 -0.8015075683593804 +1.822375 -0.2408447265625 -0.047760009765625 -0.8015075683593804 +1.8225 -0.2408447265625 -0.047760009765625 -0.8015075683593804 +1.822625 -0.24542236328125 -0.048675537109375 -0.8015075683593804 +1.82275 -0.24542236328125 -0.048675537109375 -0.8015075683593804 +1.822875 -0.249969482421875 -0.049591064453125 -0.8015075683593804 +1.823 -0.25445556640625 -0.05047607421875 -0.8015075683593804 +1.823125 -0.25445556640625 -0.05047607421875 -0.8015075683593804 +1.82325 -0.25897216796875 -0.051361083984375 -0.8015075683593804 +1.823375 -0.25897216796875 -0.051361083984375 -0.8015075683593804 +1.82350000000000016 -0.263427734375 -0.05224609375 -0.8015075683593804 +1.823625 -0.267852783203125 -0.053131103515625 -0.8015075683593804 +1.82375 -0.267852783203125 -0.053131103515625 -0.8015075683593804 +1.82387500000000016 -0.27227783203125 -0.05401611328125 -0.8015075683593804 +1.824 -0.27227783203125 -0.0076904296875 -0.9715881347656272 +1.824125 -0.276641845703125 -0.0078125 -0.9715881347656272 +1.82425 -0.280975341796875 -0.0079345703125 -0.9715881347656272 +1.824375 -0.280975341796875 -0.0079345703125 -0.9715881347656272 +1.8245 -0.285308837890625 -0.008056640625 -0.9715881347656272 +1.824625 -0.285308837890625 -0.008056640625 -0.9715881347656272 +1.82475 -0.289581298828125 -0.0081787109375 -0.9715881347656272 +1.824875 -0.2938232421875 -0.00830078125 -0.9715881347656272 +1.825 -0.2938232421875 -0.00830078125 -0.9715881347656272 +1.825125 -0.298065185546875 -0.0084228515625 -0.9715881347656272 +1.82525 -0.298065185546875 -0.0084228515625 -0.9715881347656272 +1.825375 -0.30224609375 -0.008544921875 -0.9715881347656272 +1.82550000000000016 -0.306396484375 -0.0086669921875 -0.9715881347656272 +1.825625 -0.306396484375 -0.0086669921875 -0.9715881347656272 +1.82575 -0.310516357421875 -0.0087890625 -0.9715881347656272 +1.82587500000000016 -0.310516357421875 -0.0087890625 -0.9715881347656272 +1.826 -0.314605712890625 -0.008880615234375 -0.9715881347656272 +1.826125 -0.31866455078125 -0.009002685546875 -0.9715881347656272 +1.82625 -0.31866455078125 -0.009002685546875 -0.9715881347656272 +1.826375 -0.322662353515625 -0.009124755859375 -0.9715881347656272 +1.8265 -0.322662353515625 -0.009124755859375 -0.9715881347656272 +1.826625 -0.32666015625 -0.009246826171875 -0.9715881347656272 +1.82675 -0.330596923828125 -0.00933837890625 -0.9715881347656272 +1.826875 -0.330596923828125 -0.00933837890625 -0.9715881347656272 +1.827 -0.334503173828125 -0.00946044921875 -0.9715881347656272 +1.827125 -0.334503173828125 -0.00946044921875 -0.9715881347656272 +1.82725 -0.33837890625 -0.00958251953125 -0.9715881347656272 +1.827375 -0.34222412109375 -0.009674072265625 -0.9715881347656272 +1.82750000000000016 -0.34222412109375 -0.009674072265625 -0.9715881347656272 +1.827625 -0.34600830078125 -0.009796142578125 -0.9715881347656272 +1.82775 -0.34600830078125 -0.009796142578125 -0.9715881347656272 +1.82787500000000016 -0.349761962890625 -0.0098876953125 -0.9715881347656272 +1.828 -0.353485107421875 -0.010009765625 -0.9715881347656272 +1.828125 -0.353485107421875 -0.010009765625 -0.9715881347656272 +1.82825 -0.357177734375 -0.010101318359375 -0.9715881347656272 +1.828375 -0.357177734375 -0.010101318359375 -0.9715881347656272 +1.8285 -0.360809326171875 -0.01019287109375 -0.9715881347656272 +1.828625 -0.36444091796875 -0.01031494140625 -0.9715881347656272 +1.82875 -0.36444091796875 -0.01031494140625 -0.9715881347656272 +1.828875 -0.36798095703125 -0.010406494140625 -0.9715881347656272 +1.829 -0.36798095703125 -0.010406494140625 -0.9715881347656272 +1.82912500000000016 -0.37152099609375 -0.010498046875 -0.9715881347656272 +1.82925 -0.375 -0.0106201171875 -0.9715881347656272 +1.829375 -0.375 -0.0106201171875 -0.9715881347656272 +1.82950000000000016 -0.378448486328125 -0.010711669921875 -0.9715881347656272 +1.829625 -0.378448486328125 -0.010711669921875 -0.9715881347656272 +1.82975 -0.3818359375 -0.01080322265625 -0.9715881347656272 +1.82987500000000016 -0.38519287109375 -0.010894775390625 -0.9715881347656272 +1.83 -0.38519287109375 -0.010894775390625 -0.9715881347656272 +1.830125 -0.388519287109375 -0.010986328125 -0.9715881347656272 +1.83025 -0.388519287109375 -0.010986328125 -0.9715881347656272 +1.830375 -0.39178466796875 -0.011077880859375 -0.9715881347656272 +1.8305 -0.39501953125 -0.01116943359375 -0.9715881347656272 +1.830625 -0.39501953125 -0.01116943359375 -0.9715881347656272 +1.83075 -0.398193359375 -0.011260986328125 -0.9715881347656272 +1.830875 -0.398193359375 -0.011260986328125 -0.9715881347656272 +1.831 -0.401336669921875 -0.0113525390625 -0.9715881347656272 +1.83112500000000016 -0.404449462890625 -0.011444091796875 -0.9715881347656272 +1.83125 -0.404449462890625 -0.011444091796875 -0.9715881347656272 +1.831375 -0.407501220703125 -0.01153564453125 -0.9715881347656272 +1.83150000000000016 -0.407501220703125 -0.01153564453125 -0.9715881347656272 +1.831625 -0.4105224609375 -0.0115966796875 -0.9715881347656272 +1.83175 -0.413482666015625 -0.011688232421875 -0.9715881347656272 +1.83187500000000016 -0.413482666015625 -0.011688232421875 -0.9715881347656272 +1.832 -0.416412353515625 -0.01177978515625 -0.9715881347656272 +1.832125 -0.416412353515625 -0.01177978515625 -0.9715881347656272 +1.83225 -0.419281005859375 -0.011871337890625 -0.9715881347656272 +1.832375 -0.422119140625 -0.011932373046875 -0.9715881347656272 +1.8325 -0.422119140625 -0.011932373046875 -0.9715881347656272 +1.832625 -0.424896240234375 -0.01202392578125 -0.9715881347656272 +1.83275 -0.424896240234375 -0.01202392578125 -0.9715881347656272 +1.832875 -0.4276123046875 -0.0120849609375 -0.9715881347656272 +1.833 -0.4302978515625 -0.012176513671875 -0.9715881347656272 +1.83312500000000016 -0.4302978515625 -0.012176513671875 -0.9715881347656272 +1.83325 -0.432952880859375 -0.012237548828125 -0.9715881347656272 +1.833375 -0.432952880859375 -0.012237548828125 -0.9715881347656272 +1.83350000000000016 -0.435546875 -0.0123291015625 -0.9715881347656272 +1.833625 -0.438079833984375 -0.01239013671875 -0.9715881347656272 +1.83375 -0.438079833984375 -0.01239013671875 -0.9715881347656272 +1.83387500000000016 -0.440582275390625 -0.012451171875 -0.9715881347656272 +1.834 -0.440582275390625 -0.012451171875 -0.9715881347656272 +1.834125 -0.44305419921875 -0.012542724609375 -0.9715881347656272 +1.83425 -0.4454345703125 -0.012603759765625 -0.9715881347656272 +1.834375 -0.4454345703125 -0.012603759765625 -0.9715881347656272 +1.8345 -0.447784423828125 -0.012664794921875 -0.9715881347656272 +1.834625 -0.447784423828125 -0.012664794921875 -0.9715881347656272 +1.83475 -0.450103759765625 -0.012725830078125 -0.9715881347656272 +1.834875 -0.452362060546875 -0.012786865234375 -0.9715881347656272 +1.835 -0.452362060546875 -0.012786865234375 -0.9715881347656272 +1.83512500000000016 -0.454559326171875 -0.012847900390625 -0.9715881347656272 +1.83525 -0.454559326171875 -0.012847900390625 -0.9715881347656272 +1.835375 -0.45672607421875 -0.012908935546875 -0.9715881347656272 +1.83550000000000016 -0.458831787109375 -0.012969970703125 -0.9715881347656272 +1.835625 -0.458831787109375 -0.012969970703125 -0.9715881347656272 +1.83575 -0.46087646484375 -0.013031005859375 -0.9715881347656272 +1.835875000000000256 -0.46087646484375 -0.013031005859375 -0.9715881347656272 +1.836 -0.462890625 -0.013092041015625 -0.9715881347656272 +1.836125 -0.464813232421875 -0.013153076171875 -0.9715881347656272 +1.83625 -0.464813232421875 -0.013153076171875 -0.9715881347656272 +1.836375 -0.46673583984375 -0.013214111328125 -0.9715881347656272 +1.8365 -0.46673583984375 -0.013214111328125 -0.9715881347656272 +1.836625 -0.46856689453125 -0.01324462890625 -0.9715881347656272 +1.83675 -0.470367431640625 -0.0133056640625 -0.9715881347656272 +1.836875 -0.470367431640625 -0.0133056640625 -0.9715881347656272 +1.837 -0.472137451171875 -0.01336669921875 -0.9715881347656272 +1.83712500000000016 -0.472137451171875 -0.01336669921875 -0.9715881347656272 +1.83725 -0.47381591796875 -0.013397216796875 -0.9715881347656272 +1.837375 -0.4754638671875 -0.013458251953125 -0.9715881347656272 +1.83750000000000016 -0.4754638671875 -0.013458251953125 -0.9715881347656272 +1.837625 -0.47705078125 -0.01348876953125 -0.9715881347656272 +1.83775 -0.47705078125 -0.01348876953125 -0.9715881347656272 +1.837875000000000256 -0.478607177734375 -0.0135498046875 -0.9715881347656272 +1.838 -0.480072021484375 -0.013580322265625 -0.9715881347656272 +1.838125 -0.480072021484375 -0.013580322265625 -0.9715881347656272 +1.83825 -0.48150634765625 -0.01361083984375 -0.9715881347656272 +1.838375 -0.48150634765625 -0.01361083984375 -0.9715881347656272 +1.8385 -0.48291015625 -0.013671875 -0.9715881347656272 +1.838625 -0.484222412109375 -0.013702392578125 -0.9715881347656272 +1.83875 -0.484222412109375 -0.013702392578125 -0.9715881347656272 +1.838875 -0.485504150390625 -0.01373291015625 -0.9715881347656272 +1.839 -0.485504150390625 -0.01373291015625 -0.9715881347656272 +1.83912500000000016 -0.486724853515625 -0.013763427734375 -0.9715881347656272 +1.83925 -0.487884521484375 -0.0137939453125 -0.9715881347656272 +1.839375 -0.487884521484375 -0.0137939453125 -0.9715881347656272 +1.83950000000000016 -0.489013671875 -0.013824462890625 -0.9715881347656272 +1.839625 -0.489013671875 -0.013824462890625 -0.9715881347656272 +1.83975 -0.490081787109375 -0.01385498046875 -0.9715881347656272 +1.839875000000000256 -0.4910888671875 -0.013885498046875 -0.9715881347656272 +1.84 -0.4910888671875 -0.013885498046875 -0.9715881347656272 +1.840125 -0.492034912109375 -0.013916015625 -0.9715881347656272 +1.84025 -0.492034912109375 -0.013916015625 -0.9715881347656272 +1.840375 -0.492950439453125 -0.013946533203125 -0.9715881347656272 +1.8405 -0.4937744140625 -0.01397705078125 -0.9715881347656272 +1.840625 -0.4937744140625 -0.01397705078125 -0.9715881347656272 +1.84075 -0.49456787109375 -0.014007568359375 -0.9715881347656272 +1.840875 -0.49456787109375 -0.014007568359375 -0.9715881347656272 +1.841 -0.49530029296875 -0.014007568359375 -0.9715881347656272 +1.84112500000000016 -0.496002197265625 -0.0140380859375 -0.9715881347656272 +1.84125 -0.496002197265625 -0.0140380859375 -0.9715881347656272 +1.841375 -0.496612548828125 -0.0140380859375 -0.9715881347656272 +1.84150000000000016 -0.496612548828125 -0.0140380859375 -0.9715881347656272 +1.841625 -0.4971923828125 -0.014068603515625 -0.9715881347656272 +1.84175 -0.497711181640625 -0.014068603515625 -0.9715881347656272 +1.841875 -0.497711181640625 -0.014068603515625 -0.9715881347656272 +1.842 -0.498199462890625 -0.01409912109375 -0.9715881347656272 +1.842125 -0.498199462890625 -0.01409912109375 -0.9715881347656272 +1.84225 -0.49859619140625 -0.01409912109375 -0.9715881347656272 +1.842375 -0.49896240234375 -0.014129638671875 -0.9715881347656272 +1.8425 -0.49896240234375 -0.014129638671875 -0.9715881347656272 +1.842625 -0.499267578125 -0.014129638671875 -0.9715881347656272 +1.84275 -0.499267578125 -0.014129638671875 -0.9715881347656272 +1.842875 -0.49951171875 -0.014129638671875 -0.9715881347656272 +1.843 -0.49969482421875 -0.014129638671875 -0.9715881347656272 +1.84312500000000016 -0.49969482421875 -0.014129638671875 -0.9715881347656272 +1.84325 -0.49981689453125 -0.014129638671875 -0.9715881347656272 +1.843375 -0.49981689453125 -0.014129638671875 -0.9715881347656272 +1.84350000000000016 -0.499908447265625 -0.014129638671875 -0.9715881347656272 +1.843625 -0.49993896484375 -0.014129638671875 -0.9715881347656272 +1.84375 -0.49993896484375 -0.014129638671875 -0.9715881347656272 +1.843875 -0.499908447265625 -0.014129638671875 -0.9715881347656272 +1.844 -0.499908447265625 -0.014129638671875 -0.9715881347656272 +1.844125 -0.49981689453125 -0.014129638671875 -0.9715881347656272 +1.84425 -0.49969482421875 -0.014129638671875 -0.9715881347656272 +1.844375 -0.49969482421875 -0.014129638671875 -0.9715881347656272 +1.8445 -0.49951171875 -0.014129638671875 -0.9715881347656272 +1.844625 -0.49951171875 -0.014129638671875 -0.9715881347656272 +1.84475000000000016 -0.499267578125 -0.014129638671875 -0.9715881347656272 +1.844875 -0.49896240234375 -0.014129638671875 -0.9715881347656272 +1.845 -0.49896240234375 -0.014129638671875 -0.9715881347656272 +1.84512500000000016 -0.49859619140625 -0.01409912109375 -0.9715881347656272 +1.84525 -0.49859619140625 -0.01409912109375 -0.9715881347656272 +1.845375 -0.498199462890625 -0.01409912109375 -0.9715881347656272 +1.84550000000000016 -0.497711181640625 -0.014068603515625 -0.9715881347656272 +1.845625 -0.497711181640625 -0.014068603515625 -0.9715881347656272 +1.84575 -0.4971923828125 -0.014068603515625 -0.9715881347656272 +1.845875 -0.4971923828125 -0.014068603515625 -0.9715881347656272 +1.846 -0.496612548828125 -0.0140380859375 -0.9715881347656272 +1.846125 -0.496002197265625 -0.0140380859375 -0.9715881347656272 +1.84625 -0.496002197265625 -0.0140380859375 -0.9715881347656272 +1.846375 -0.49530029296875 -0.014007568359375 -0.9715881347656272 +1.8465 -0.49530029296875 -0.014007568359375 -0.9715881347656272 +1.846625 -0.49456787109375 -0.014007568359375 -0.9715881347656272 +1.84675000000000016 -0.4937744140625 -0.01397705078125 -0.9715881347656272 +1.846875 -0.4937744140625 -0.01397705078125 -0.9715881347656272 +1.847 -0.492950439453125 -0.013946533203125 -0.9715881347656272 +1.84712500000000016 -0.492950439453125 -0.013946533203125 -0.9715881347656272 +1.84725 -0.492034912109375 -0.013916015625 -0.9715881347656272 +1.847375 -0.4910888671875 -0.013885498046875 -0.9715881347656272 +1.84750000000000016 -0.4910888671875 -0.013885498046875 -0.9715881347656272 +1.847625 -0.490081787109375 -0.01385498046875 -0.9715881347656272 +1.84775 -0.490081787109375 -0.01385498046875 -0.9715881347656272 +1.847875 -0.489013671875 -0.013824462890625 -0.9715881347656272 +1.848 -0.487884521484375 -0.0137939453125 -0.9715881347656272 +1.848125 -0.487884521484375 -0.0137939453125 -0.9715881347656272 +1.84825 -0.486724853515625 -0.013763427734375 -0.9715881347656272 +1.848375 -0.486724853515625 -0.013763427734375 -0.9715881347656272 +1.8485 -0.485504150390625 -0.01373291015625 -0.9715881347656272 +1.848625 -0.484222412109375 -0.013702392578125 -0.9715881347656272 +1.84875000000000016 -0.484222412109375 -0.013702392578125 -0.9715881347656272 +1.848875 -0.48291015625 -0.013671875 -0.9715881347656272 +1.849 -0.48291015625 -0.013671875 -0.9715881347656272 +1.84912500000000016 -0.48150634765625 -0.01361083984375 -0.9715881347656272 +1.84925 -0.480072021484375 -0.013580322265625 -0.9715881347656272 +1.849375 -0.480072021484375 -0.013580322265625 -0.9715881347656272 +1.84950000000000016 -0.478607177734375 -0.0135498046875 -0.9715881347656272 +1.849625 -0.478607177734375 -0.0135498046875 -0.9715881347656272 +1.84975 -0.47705078125 -0.01348876953125 -0.9715881347656272 +1.849875 -0.4754638671875 -0.013458251953125 -0.9715881347656272 +1.85 -0.4754638671875 -0.013458251953125 -0.9715881347656272 +1.850125 -0.47381591796875 -0.013397216796875 -0.9715881347656272 +1.85025 -0.47381591796875 -0.013397216796875 -0.9715881347656272 +1.850375 -0.472137451171875 -0.01336669921875 -0.9715881347656272 +1.8505 -0.470367431640625 -0.0133056640625 -0.9715881347656272 +1.850625 -0.470367431640625 -0.0133056640625 -0.9715881347656272 +1.85075000000000016 -0.46856689453125 -0.01324462890625 -0.9715881347656272 +1.850875 -0.46856689453125 -0.01324462890625 -0.9715881347656272 +1.851 -0.46673583984375 -0.013214111328125 -0.9715881347656272 +1.85112500000000016 -0.464813232421875 -0.013153076171875 -0.9715881347656272 +1.85125 -0.464813232421875 -0.013153076171875 -0.9715881347656272 +1.851375 -0.462890625 -0.013092041015625 -0.9715881347656272 +1.851500000000000256 -0.462890625 -0.013092041015625 -0.9715881347656272 +1.851625 -0.46087646484375 -0.013031005859375 -0.9715881347656272 +1.85175 -0.458831787109375 -0.012969970703125 -0.9715881347656272 +1.851875 -0.458831787109375 -0.012969970703125 -0.9715881347656272 +1.852 -0.45672607421875 -0.012908935546875 -0.9715881347656272 +1.852125 -0.45672607421875 -0.012908935546875 -0.9715881347656272 +1.85225 -0.454559326171875 -0.012847900390625 -0.9715881347656272 +1.852375 -0.452362060546875 -0.012786865234375 -0.9715881347656272 +1.8525 -0.452362060546875 -0.012786865234375 -0.9715881347656272 +1.852625 -0.450103759765625 -0.012725830078125 -0.9715881347656272 +1.85275000000000016 -0.450103759765625 -0.012725830078125 -0.9715881347656272 +1.852875 -0.447784423828125 -0.012664794921875 -0.9715881347656272 +1.853 -0.4454345703125 -0.012603759765625 -0.9715881347656272 +1.85312500000000016 -0.4454345703125 -0.012603759765625 -0.9715881347656272 +1.85325 -0.44305419921875 -0.012542724609375 -0.9715881347656272 +1.853375 -0.44305419921875 -0.012542724609375 -0.9715881347656272 +1.853500000000000256 -0.440582275390625 -0.012451171875 -0.9715881347656272 +1.853625 -0.438079833984375 -0.01239013671875 -0.9715881347656272 +1.85375 -0.438079833984375 -0.01239013671875 -0.9715881347656272 +1.853875 -0.435546875 -0.0123291015625 -0.9715881347656272 +1.854 -0.435546875 -0.0123291015625 -0.9715881347656272 +1.854125 -0.432952880859375 -0.012237548828125 -0.9715881347656272 +1.85425 -0.4302978515625 -0.012176513671875 -0.9715881347656272 +1.854375 -0.4302978515625 -0.012176513671875 -0.9715881347656272 +1.8545 -0.4276123046875 -0.0120849609375 -0.9715881347656272 +1.854625 -0.4276123046875 -0.0120849609375 -0.9715881347656272 +1.85475000000000016 -0.424896240234375 -0.01202392578125 -0.9715881347656272 +1.854875 -0.422119140625 -0.011932373046875 -0.9715881347656272 +1.855 -0.422119140625 -0.011932373046875 -0.9715881347656272 +1.85512500000000016 -0.419281005859375 -0.011871337890625 -0.9715881347656272 +1.85525 -0.419281005859375 -0.011871337890625 -0.9715881347656272 +1.855375 -0.416412353515625 -0.01177978515625 -0.9715881347656272 +1.855500000000000256 -0.413482666015625 -0.011688232421875 -0.9715881347656272 +1.855625 -0.413482666015625 -0.011688232421875 -0.9715881347656272 +1.85575 -0.4105224609375 -0.0115966796875 -0.9715881347656272 +1.855875 -0.4105224609375 -0.0115966796875 -0.9715881347656272 +1.856 -0.407501220703125 -0.005401611328125 -0.9866333007812484 +1.856125 -0.404449462890625 -0.005340576171875 -0.9866333007812484 +1.85625 -0.404449462890625 -0.005340576171875 -0.9866333007812484 +1.856375 -0.401336669921875 -0.00531005859375 -0.9866333007812484 +1.8565 -0.401336669921875 -0.00531005859375 -0.9866333007812484 +1.856625 -0.398193359375 -0.005279541015625 -0.9866333007812484 +1.85675000000000016 -0.39501953125 -0.005218505859375 -0.9866333007812484 +1.856875 -0.39501953125 -0.005218505859375 -0.9866333007812484 +1.857 -0.39178466796875 -0.00518798828125 -0.9866333007812484 +1.85712500000000016 -0.39178466796875 -0.00518798828125 -0.9866333007812484 +1.85725 -0.388519287109375 -0.005157470703125 -0.9866333007812484 +1.857375 -0.38519287109375 -0.005096435546875 -0.9866333007812484 +1.8575 -0.38519287109375 -0.005096435546875 -0.9866333007812484 +1.857625 -0.3818359375 -0.00506591796875 -0.9866333007812484 +1.85775 -0.3818359375 -0.00506591796875 -0.9866333007812484 +1.857875 -0.378448486328125 -0.0050048828125 -0.9866333007812484 +1.858 -0.375 -0.004974365234375 -0.9866333007812484 +1.858125 -0.375 -0.004974365234375 -0.9866333007812484 +1.85825 -0.37152099609375 -0.004913330078125 -0.9866333007812484 +1.858375 -0.37152099609375 -0.004913330078125 -0.9866333007812484 +1.8585 -0.36798095703125 -0.0048828125 -0.9866333007812484 +1.858625 -0.36444091796875 -0.00482177734375 -0.9866333007812484 +1.85875000000000016 -0.36444091796875 -0.00482177734375 -0.9866333007812484 +1.858875 -0.360809326171875 -0.0047607421875 -0.9866333007812484 +1.859 -0.360809326171875 -0.0047607421875 -0.9866333007812484 +1.85912500000000016 -0.357177734375 -0.004730224609375 -0.9866333007812484 +1.85925 -0.353485107421875 -0.004669189453125 -0.9866333007812484 +1.859375 -0.353485107421875 -0.004669189453125 -0.9866333007812484 +1.8595 -0.349761962890625 -0.004638671875 -0.9866333007812484 +1.859625 -0.349761962890625 -0.004638671875 -0.9866333007812484 +1.85975 -0.34600830078125 -0.00457763671875 -0.9866333007812484 +1.859875 -0.34222412109375 -0.0045166015625 -0.9866333007812484 +1.86 -0.34222412109375 -0.0045166015625 -0.9866333007812484 +1.860125 -0.33837890625 -0.004486083984375 -0.9866333007812484 +1.86025 -0.33837890625 -0.004486083984375 -0.9866333007812484 +1.86037500000000016 -0.334503173828125 -0.004425048828125 -0.9866333007812484 +1.8605 -0.330596923828125 -0.004364013671875 -0.9866333007812484 +1.860625 -0.330596923828125 -0.004364013671875 -0.9866333007812484 +1.86075000000000016 -0.32666015625 -0.00433349609375 -0.9866333007812484 +1.860875 -0.32666015625 -0.00433349609375 -0.9866333007812484 +1.861 -0.322662353515625 -0.0042724609375 -0.9866333007812484 +1.86112500000000016 -0.31866455078125 -0.00421142578125 -0.9866333007812484 +1.86125 -0.31866455078125 -0.00421142578125 -0.9866333007812484 +1.861375 -0.314605712890625 -0.004150390625 -0.9866333007812484 +1.8615 -0.314605712890625 -0.004150390625 -0.9866333007812484 +1.861625 -0.310516357421875 -0.004119873046875 -0.9866333007812484 +1.86175 -0.306396484375 -0.004058837890625 -0.9866333007812484 +1.861875 -0.306396484375 -0.004058837890625 -0.9866333007812484 +1.862 -0.30224609375 -0.003997802734375 -0.9866333007812484 +1.862125 -0.30224609375 -0.003997802734375 -0.9866333007812484 +1.86225 -0.298065185546875 -0.003936767578125 -0.9866333007812484 +1.86237500000000016 -0.2938232421875 -0.003875732421875 -0.9866333007812484 +1.8625 -0.2938232421875 -0.003875732421875 -0.9866333007812484 +1.862625 -0.289581298828125 -0.003814697265625 -0.9866333007812484 +1.86275000000000016 -0.289581298828125 -0.003814697265625 -0.9866333007812484 +1.862875 -0.285308837890625 -0.0037841796875 -0.9866333007812484 +1.863 -0.280975341796875 -0.00372314453125 -0.9866333007812484 +1.86312500000000016 -0.280975341796875 -0.00372314453125 -0.9866333007812484 +1.86325 -0.276641845703125 -0.003662109375 -0.9866333007812484 +1.863375 -0.276641845703125 -0.003662109375 -0.9866333007812484 +1.8635 -0.27227783203125 -0.00360107421875 -0.9866333007812484 +1.863625 -0.267852783203125 -0.0035400390625 -0.9866333007812484 +1.86375 -0.267852783203125 -0.0035400390625 -0.9866333007812484 +1.863875 -0.263427734375 -0.00347900390625 -0.9866333007812484 +1.864 -0.263427734375 -0.00347900390625 -0.9866333007812484 +1.864125 -0.25897216796875 -0.00341796875 -0.9866333007812484 +1.86425 -0.25445556640625 -0.00335693359375 -0.9866333007812484 +1.86437500000000016 -0.25445556640625 -0.00335693359375 -0.9866333007812484 +1.8645 -0.249969482421875 -0.0032958984375 -0.9866333007812484 +1.864625 -0.249969482421875 -0.0032958984375 -0.9866333007812484 +1.86475000000000016 -0.24542236328125 -0.00323486328125 -0.9866333007812484 +1.864875 -0.2408447265625 -0.003173828125 -0.9866333007812484 +1.865 -0.2408447265625 -0.003173828125 -0.9866333007812484 +1.86512500000000016 -0.236236572265625 -0.00311279296875 -0.9866333007812484 +1.86525 -0.236236572265625 -0.00311279296875 -0.9866333007812484 +1.865375 -0.231597900390625 -0.0030517578125 -0.9866333007812484 +1.8655 -0.226959228515625 -0.00299072265625 -0.9866333007812484 +1.865625 -0.226959228515625 -0.00299072265625 -0.9866333007812484 +1.86575 -0.2222900390625 -0.0029296875 -0.9866333007812484 +1.865875 -0.2222900390625 -0.0029296875 -0.9866333007812484 +1.866 -0.21759033203125 -0.00286865234375 -0.9866333007812484 +1.866125 -0.212860107421875 -0.0028076171875 -0.9866333007812484 +1.86625 -0.212860107421875 -0.0028076171875 -0.9866333007812484 +1.86637500000000016 -0.208099365234375 -0.00274658203125 -0.9866333007812484 +1.8665 -0.208099365234375 -0.00274658203125 -0.9866333007812484 +1.866625 -0.203338623046875 -0.002685546875 -0.9866333007812484 +1.86675000000000016 -0.19854736328125 -0.00262451171875 -0.9866333007812484 +1.866875 -0.19854736328125 -0.00262451171875 -0.9866333007812484 +1.867 -0.1937255859375 -0.0025634765625 -0.9866333007812484 +1.867125000000000256 -0.1937255859375 -0.0025634765625 -0.9866333007812484 +1.86725 -0.188873291015625 -0.00250244140625 -0.9866333007812484 +1.867375 -0.18402099609375 -0.00244140625 -0.9866333007812484 +1.8675 -0.18402099609375 -0.00244140625 -0.9866333007812484 +1.867625 -0.17913818359375 -0.002349853515625 -0.9866333007812484 +1.86775 -0.17913818359375 -0.002349853515625 -0.9866333007812484 +1.867875 -0.17425537109375 -0.002288818359375 -0.9866333007812484 +1.868 -0.169342041015625 -0.002227783203125 -0.9866333007812484 +1.868125 -0.169342041015625 -0.002227783203125 -0.9866333007812484 +1.86825 -0.164398193359375 -0.002166748046875 -0.9866333007812484 +1.86837500000000016 -0.164398193359375 -0.002166748046875 -0.9866333007812484 +1.8685 -0.159454345703125 -0.002105712890625 -0.9866333007812484 +1.868625 -0.15447998046875 -0.002044677734375 -0.9866333007812484 +1.86875000000000016 -0.15447998046875 -0.002044677734375 -0.9866333007812484 +1.868875 -0.14947509765625 -0.001983642578125 -0.9866333007812484 +1.869 -0.14947509765625 -0.001983642578125 -0.9866333007812484 +1.869125000000000256 -0.14447021484375 -0.00189208984375 -0.9866333007812484 +1.86925 -0.13946533203125 -0.0018310546875 -0.9866333007812484 +1.869375 -0.13946533203125 -0.0018310546875 -0.9866333007812484 +1.8695 -0.134429931640625 -0.00177001953125 -0.9866333007812484 +1.869625 -0.134429931640625 -0.00177001953125 -0.9866333007812484 +1.86975 -0.129364013671875 -0.001708984375 -0.9866333007812484 +1.869875 -0.124298095703125 -0.00164794921875 -0.9866333007812484 +1.87 -0.124298095703125 -0.00164794921875 -0.9866333007812484 +1.870125 -0.119232177734375 -0.001556396484375 -0.9866333007812484 +1.87025 -0.119232177734375 -0.001556396484375 -0.9866333007812484 +1.87037500000000016 -0.1141357421875 -0.001495361328125 -0.9866333007812484 +1.8705 -0.109039306640625 -0.001434326171875 -0.9866333007812484 +1.870625 -0.109039306640625 -0.001434326171875 -0.9866333007812484 +1.87075000000000016 -0.103912353515625 -0.001373291015625 -0.9866333007812484 +1.870875 -0.103912353515625 -0.001373291015625 -0.9866333007812484 +1.871 -0.098785400390625 -0.00128173828125 -0.9866333007812484 +1.871125000000000256 -0.093658447265625 -0.001220703125 -0.9866333007812484 +1.87125 -0.093658447265625 -0.001220703125 -0.9866333007812484 +1.871375 -0.0885009765625 -0.00115966796875 -0.9866333007812484 +1.8715 -0.0885009765625 -0.00115966796875 -0.9866333007812484 +1.871625 -0.083343505859375 -0.0010986328125 -0.9866333007812484 +1.87175 -0.07818603515625 -0.00103759765625 -0.9866333007812484 +1.871875 -0.07818603515625 -0.00103759765625 -0.9866333007812484 +1.872 -0.072998046875 -0.000946044921875 -0.9866333007812484 +1.872125 -0.072998046875 -0.000946044921875 -0.9866333007812484 +1.87225 -0.06781005859375 -0.000885009765625 -0.9866333007812484 +1.87237500000000016 -0.0626220703125 -0.000823974609375 -0.9866333007812484 +1.8725 -0.0626220703125 -0.000823974609375 -0.9866333007812484 +1.872625 -0.05743408203125 -0.000732421875 -0.9866333007812484 +1.87275000000000016 -0.05743408203125 -0.000732421875 -0.9866333007812484 +1.872875 -0.05224609375 -0.00067138671875 -0.9866333007812484 +1.873 -0.047027587890625 -0.0006103515625 -0.9866333007812484 +1.873125 -0.047027587890625 -0.0006103515625 -0.9866333007812484 +1.87325 -0.04180908203125 -0.00054931640625 -0.9866333007812484 +1.873375 -0.04180908203125 -0.00054931640625 -0.9866333007812484 +1.8735 -0.036590576171875 -0.000457763671875 -0.9866333007812484 +1.873625 -0.0313720703125 -0.000396728515625 -0.9866333007812484 +1.87375 -0.0313720703125 -0.000396728515625 -0.9866333007812484 +1.873875 -0.026123046875 -0.000335693359375 -0.9866333007812484 +1.874 -0.026123046875 -0.000335693359375 -0.9866333007812484 +1.874125 -0.020904541015625 -0.000274658203125 -0.9866333007812484 +1.87425 -0.01568603515625 -0.00018310546875 -0.9866333007812484 +1.87437500000000016 -0.01568603515625 -0.00018310546875 -0.9866333007812484 +1.8745 -0.01043701171875 -0.0001220703125 -0.9866333007812484 +1.874625 -0.01043701171875 -0.0001220703125 -0.9866333007812484 +1.87475000000000016 -0.005218505859375 -6.103515625e-05 -0.9866333007812484 +1.874875 0.0 0.0 -0.9866333007812484 +1.875 0.0 0.0 -0.9866333007812484 +1.875125 0.005218505859375 6.103515625e-05 -0.9866333007812484 +1.87525 0.005218505859375 6.103515625e-05 -0.9866333007812484 +1.875375 0.01043701171875 0.0001220703125 -0.9866333007812484 +1.8755 0.01568603515625 0.00018310546875 -0.9866333007812484 +1.875625 0.01568603515625 0.00018310546875 -0.9866333007812484 +1.87575 0.020904541015625 0.000274658203125 -0.9866333007812484 +1.875875 0.020904541015625 0.000274658203125 -0.9866333007812484 +1.87600000000000016 0.026123046875 0.000335693359375 -0.9866333007812484 +1.876125 0.0313720703125 0.000396728515625 -0.9866333007812484 +1.87625 0.0313720703125 0.000396728515625 -0.9866333007812484 +1.87637500000000016 0.036590576171875 0.000457763671875 -0.9866333007812484 +1.8765 0.036590576171875 0.000457763671875 -0.9866333007812484 +1.876625 0.04180908203125 0.00054931640625 -0.9866333007812484 +1.87675000000000016 0.047027587890625 0.0006103515625 -0.9866333007812484 +1.876875 0.047027587890625 0.0006103515625 -0.9866333007812484 +1.877 0.05224609375 0.00067138671875 -0.9866333007812484 +1.877125 0.05224609375 0.00067138671875 -0.9866333007812484 +1.87725 0.05743408203125 0.000732421875 -0.9866333007812484 +1.877375 0.0626220703125 0.000823974609375 -0.9866333007812484 +1.8775 0.0626220703125 0.000823974609375 -0.9866333007812484 +1.877625 0.06781005859375 0.000885009765625 -0.9866333007812484 +1.87775 0.06781005859375 0.000885009765625 -0.9866333007812484 +1.877875 0.072998046875 0.000946044921875 -0.9866333007812484 +1.87800000000000016 0.07818603515625 0.00103759765625 -0.9866333007812484 +1.878125 0.07818603515625 0.00103759765625 -0.9866333007812484 +1.87825 0.083343505859375 0.0010986328125 -0.9866333007812484 +1.87837500000000016 0.083343505859375 0.0010986328125 -0.9866333007812484 +1.8785 0.0885009765625 0.00115966796875 -0.9866333007812484 +1.878625 0.093658447265625 0.001220703125 -0.9866333007812484 +1.87875000000000016 0.093658447265625 0.001220703125 -0.9866333007812484 +1.878875 0.098785400390625 0.00128173828125 -0.9866333007812484 +1.879 0.098785400390625 0.00128173828125 -0.9866333007812484 +1.879125 0.103912353515625 0.001373291015625 -0.9866333007812484 +1.87925 0.109039306640625 0.001434326171875 -0.9866333007812484 +1.879375 0.109039306640625 0.001434326171875 -0.9866333007812484 +1.8795 0.1141357421875 0.001495361328125 -0.9866333007812484 +1.879625 0.1141357421875 0.001495361328125 -0.9866333007812484 +1.87975 0.119232177734375 0.001556396484375 -0.9866333007812484 +1.879875 0.124298095703125 0.00164794921875 -0.9866333007812484 +1.88000000000000016 0.124298095703125 0.00164794921875 -0.9866333007812484 +1.880125 0.129364013671875 0.001708984375 -0.9866333007812484 +1.88025 0.129364013671875 0.001708984375 -0.9866333007812484 +1.88037500000000016 0.134429931640625 0.00177001953125 -0.9866333007812484 +1.8805 0.13946533203125 0.0018310546875 -0.9866333007812484 +1.880625 0.13946533203125 0.0018310546875 -0.9866333007812484 +1.88075000000000016 0.14447021484375 0.00189208984375 -0.9866333007812484 +1.880875 0.14447021484375 0.00189208984375 -0.9866333007812484 +1.881 0.14947509765625 0.001983642578125 -0.9866333007812484 +1.881125 0.15447998046875 0.002044677734375 -0.9866333007812484 +1.88125 0.15447998046875 0.002044677734375 -0.9866333007812484 +1.881375 0.159454345703125 0.002105712890625 -0.9866333007812484 +1.8815 0.159454345703125 0.002105712890625 -0.9866333007812484 +1.881625 0.164398193359375 0.002166748046875 -0.9866333007812484 +1.88175 0.169342041015625 0.002227783203125 -0.9866333007812484 +1.881875 0.169342041015625 0.002227783203125 -0.9866333007812484 +1.88200000000000016 0.17425537109375 0.002288818359375 -0.9866333007812484 +1.882125 0.17425537109375 0.002288818359375 -0.9866333007812484 +1.88225 0.17913818359375 0.002349853515625 -0.9866333007812484 +1.88237500000000016 0.18402099609375 0.00244140625 -0.9866333007812484 +1.8825 0.18402099609375 0.00244140625 -0.9866333007812484 +1.882625 0.188873291015625 0.00250244140625 -0.9866333007812484 +1.882750000000000256 0.188873291015625 0.00250244140625 -0.9866333007812484 +1.882875 0.1937255859375 0.0025634765625 -0.9866333007812484 +1.883 0.19854736328125 0.00262451171875 -0.9866333007812484 +1.883125 0.19854736328125 0.00262451171875 -0.9866333007812484 +1.88325 0.203338623046875 0.002685546875 -0.9866333007812484 +1.883375 0.203338623046875 0.002685546875 -0.9866333007812484 +1.8835 0.208099365234375 0.00274658203125 -0.9866333007812484 +1.883625 0.212860107421875 0.0028076171875 -0.9866333007812484 +1.88375 0.212860107421875 0.0028076171875 -0.9866333007812484 +1.883875 0.21759033203125 0.00286865234375 -0.9866333007812484 +1.88400000000000016 0.21759033203125 0.00286865234375 -0.9866333007812484 +1.884125 0.2222900390625 0.0029296875 -0.9866333007812484 +1.88425 0.226959228515625 0.00299072265625 -0.9866333007812484 +1.88437500000000016 0.226959228515625 0.00299072265625 -0.9866333007812484 +1.8845 0.231597900390625 0.0030517578125 -0.9866333007812484 +1.884625 0.231597900390625 0.0030517578125 -0.9866333007812484 +1.884750000000000256 0.236236572265625 0.00311279296875 -0.9866333007812484 +1.884875 0.2408447265625 0.003173828125 -0.9866333007812484 +1.885 0.2408447265625 0.003173828125 -0.9866333007812484 +1.885125 0.24542236328125 0.00323486328125 -0.9866333007812484 +1.88525 0.24542236328125 0.00323486328125 -0.9866333007812484 +1.885375 0.249969482421875 0.0032958984375 -0.9866333007812484 +1.8855 0.25445556640625 0.00335693359375 -0.9866333007812484 +1.885625 0.25445556640625 0.00335693359375 -0.9866333007812484 +1.88575 0.25897216796875 0.00341796875 -0.9866333007812484 +1.885875 0.25897216796875 0.00341796875 -0.9866333007812484 +1.88600000000000016 0.263427734375 0.00347900390625 -0.9866333007812484 +1.886125 0.267852783203125 0.0035400390625 -0.9866333007812484 +1.88625 0.267852783203125 0.0035400390625 -0.9866333007812484 +1.88637500000000016 0.27227783203125 0.00360107421875 -0.9866333007812484 +1.8865 0.27227783203125 0.00360107421875 -0.9866333007812484 +1.886625 0.276641845703125 0.003662109375 -0.9866333007812484 +1.886750000000000256 0.280975341796875 0.00372314453125 -0.9866333007812484 +1.886875 0.280975341796875 0.00372314453125 -0.9866333007812484 +1.887 0.285308837890625 0.0037841796875 -0.9866333007812484 +1.887125 0.285308837890625 0.0037841796875 -0.9866333007812484 +1.88725 0.289581298828125 0.003814697265625 -0.9866333007812484 +1.887375 0.2938232421875 0.003875732421875 -0.9866333007812484 +1.8875 0.2938232421875 0.003875732421875 -0.9866333007812484 +1.887625 0.298065185546875 0.003936767578125 -0.9866333007812484 +1.88775 0.298065185546875 0.003936767578125 -0.9866333007812484 +1.887875 0.30224609375 0.003997802734375 -0.9866333007812484 +1.88800000000000016 0.306396484375 0.04766845703125 -0.8442993164062446 +1.888125 0.306396484375 0.04766845703125 -0.8442993164062446 +1.88825 0.310516357421875 0.048309326171875 -0.8442993164062446 +1.88837500000000016 0.310516357421875 0.048309326171875 -0.8442993164062446 +1.8885 0.314605712890625 0.0489501953125 -0.8442993164062446 +1.888625 0.31866455078125 0.049591064453125 -0.8442993164062446 +1.88875 0.31866455078125 0.049591064453125 -0.8442993164062446 +1.888875 0.322662353515625 0.050201416015625 -0.8442993164062446 +1.889 0.322662353515625 0.050201416015625 -0.8442993164062446 +1.889125 0.32666015625 0.050811767578125 -0.8442993164062446 +1.88925 0.330596923828125 0.051422119140625 -0.8442993164062446 +1.889375 0.330596923828125 0.051422119140625 -0.8442993164062446 +1.8895 0.334503173828125 0.052032470703125 -0.8442993164062446 +1.889625 0.334503173828125 0.052032470703125 -0.8442993164062446 +1.88975 0.33837890625 0.052642822265625 -0.8442993164062446 +1.889875 0.34222412109375 0.053253173828125 -0.8442993164062446 +1.89000000000000016 0.34222412109375 0.053253173828125 -0.8442993164062446 +1.890125 0.34600830078125 0.0538330078125 -0.8442993164062446 +1.89025 0.34600830078125 0.0538330078125 -0.8442993164062446 +1.89037500000000016 0.349761962890625 0.054412841796875 -0.8442993164062446 +1.8905 0.353485107421875 0.05499267578125 -0.8442993164062446 +1.890625 0.353485107421875 0.05499267578125 -0.8442993164062446 +1.89075 0.357177734375 0.055572509765625 -0.8442993164062446 +1.890875 0.357177734375 0.055572509765625 -0.8442993164062446 +1.891 0.360809326171875 0.05615234375 -0.8442993164062446 +1.891125 0.36444091796875 0.05670166015625 -0.8442993164062446 +1.89125 0.36444091796875 0.05670166015625 -0.8442993164062446 +1.891375 0.36798095703125 0.0572509765625 -0.8442993164062446 +1.8915 0.36798095703125 0.0572509765625 -0.8442993164062446 +1.89162500000000016 0.37152099609375 0.05780029296875 -0.8442993164062446 +1.89175 0.375 0.058349609375 -0.8442993164062446 +1.891875 0.375 0.058349609375 -0.8442993164062446 +1.89200000000000016 0.378448486328125 0.058868408203125 -0.8442993164062446 +1.892125 0.378448486328125 0.058868408203125 -0.8442993164062446 +1.89225 0.3818359375 0.059417724609375 -0.8442993164062446 +1.89237500000000016 0.38519287109375 0.0599365234375 -0.8442993164062446 +1.8925 0.38519287109375 0.0599365234375 -0.8442993164062446 +1.892625 0.388519287109375 0.060455322265625 -0.8442993164062446 +1.89275 0.388519287109375 0.060455322265625 -0.8442993164062446 +1.892875 0.39178466796875 0.060943603515625 -0.8442993164062446 +1.893 0.39501953125 0.06146240234375 -0.8442993164062446 +1.893125 0.39501953125 0.06146240234375 -0.8442993164062446 +1.89325 0.398193359375 0.06195068359375 -0.8442993164062446 +1.893375 0.398193359375 0.06195068359375 -0.8442993164062446 +1.8935 0.401336669921875 0.06243896484375 -0.8442993164062446 +1.89362500000000016 0.404449462890625 0.06292724609375 -0.8442993164062446 +1.89375 0.404449462890625 0.06292724609375 -0.8442993164062446 +1.893875 0.407501220703125 0.06341552734375 -0.8442993164062446 +1.89400000000000016 0.407501220703125 0.06341552734375 -0.8442993164062446 +1.894125 0.4105224609375 0.063873291015625 -0.8442993164062446 +1.89425 0.413482666015625 0.0643310546875 -0.8442993164062446 +1.89437500000000016 0.413482666015625 0.0643310546875 -0.8442993164062446 +1.8945 0.416412353515625 0.064788818359375 -0.8442993164062446 +1.894625 0.416412353515625 0.064788818359375 -0.8442993164062446 +1.89475 0.419281005859375 0.06524658203125 -0.8442993164062446 +1.894875 0.422119140625 0.065673828125 -0.8442993164062446 +1.895 0.422119140625 0.065673828125 -0.8442993164062446 +1.895125 0.424896240234375 0.06610107421875 -0.8442993164062446 +1.89525 0.424896240234375 0.06610107421875 -0.8442993164062446 +1.895375 0.4276123046875 0.0665283203125 -0.8442993164062446 +1.8955 0.4302978515625 0.06695556640625 -0.8442993164062446 +1.89562500000000016 0.4302978515625 0.06695556640625 -0.8442993164062446 +1.89575 0.432952880859375 0.067352294921875 -0.8442993164062446 +1.895875 0.432952880859375 0.067352294921875 -0.8442993164062446 +1.89600000000000016 0.435546875 0.067779541015625 -0.8442993164062446 +1.896125 0.438079833984375 0.06817626953125 -0.8442993164062446 +1.89625 0.438079833984375 0.06817626953125 -0.8442993164062446 +1.89637500000000016 0.440582275390625 0.06854248046875 -0.8442993164062446 +1.8965 0.440582275390625 0.06854248046875 -0.8442993164062446 +1.896625 0.44305419921875 0.068939208984375 -0.8442993164062446 +1.89675 0.4454345703125 0.069305419921875 -0.8442993164062446 +1.896875 0.4454345703125 0.069305419921875 -0.8442993164062446 +1.897 0.447784423828125 0.069671630859375 -0.8442993164062446 +1.897125 0.447784423828125 0.069671630859375 -0.8442993164062446 +1.89725 0.450103759765625 0.070037841796875 -0.8442993164062446 +1.897375 0.452362060546875 0.07037353515625 -0.8442993164062446 +1.8975 0.452362060546875 0.07037353515625 -0.8442993164062446 +1.89762500000000016 0.454559326171875 0.070709228515625 -0.8442993164062446 +1.89775 0.454559326171875 0.070709228515625 -0.8442993164062446 +1.897875 0.45672607421875 0.071075439453125 -0.8442993164062446 +1.89800000000000016 0.458831787109375 0.071380615234375 -0.8442993164062446 +1.898125 0.458831787109375 0.071380615234375 -0.8442993164062446 +1.89825 0.46087646484375 0.07171630859375 -0.8442993164062446 +1.898375000000000256 0.46087646484375 0.07171630859375 -0.8442993164062446 +1.8985 0.462890625 0.072021484375 -0.8442993164062446 +1.898625 0.464813232421875 0.07232666015625 -0.8442993164062446 +1.89875 0.464813232421875 0.07232666015625 -0.8442993164062446 +1.898875 0.46673583984375 0.0726318359375 -0.8442993164062446 +1.899 0.46673583984375 0.0726318359375 -0.8442993164062446 +1.899125 0.46856689453125 0.072906494140625 -0.8442993164062446 +1.89925 0.470367431640625 0.07318115234375 -0.8442993164062446 +1.899375 0.470367431640625 0.07318115234375 -0.8442993164062446 +1.8995 0.472137451171875 0.073455810546875 -0.8442993164062446 +1.89962500000000016 0.472137451171875 0.073455810546875 -0.8442993164062446 +1.89975 0.47381591796875 0.07373046875 -0.8442993164062446 +1.899875 0.4754638671875 0.073974609375 -0.8442993164062446 +1.90000000000000016 0.4754638671875 0.073974609375 -0.8442993164062446 +1.900125 0.47705078125 0.07421875 -0.8442993164062446 +1.90025 0.47705078125 0.07421875 -0.8442993164062446 +1.900375000000000256 0.478607177734375 0.074462890625 -0.8442993164062446 +1.9005 0.480072021484375 0.07470703125 -0.8442993164062446 +1.900625 0.480072021484375 0.07470703125 -0.8442993164062446 +1.90075 0.48150634765625 0.074920654296875 -0.8442993164062446 +1.900875 0.48150634765625 0.074920654296875 -0.8442993164062446 +1.901 0.48291015625 0.07513427734375 -0.8442993164062446 +1.901125 0.484222412109375 0.075347900390625 -0.8442993164062446 +1.90125 0.484222412109375 0.075347900390625 -0.8442993164062446 +1.901375 0.485504150390625 0.075531005859375 -0.8442993164062446 +1.9015 0.485504150390625 0.075531005859375 -0.8442993164062446 +1.90162500000000016 0.486724853515625 0.07574462890625 -0.8442993164062446 +1.90175 0.487884521484375 0.075897216796875 -0.8442993164062446 +1.901875 0.487884521484375 0.075897216796875 -0.8442993164062446 +1.90200000000000016 0.489013671875 0.076080322265625 -0.8442993164062446 +1.902125 0.489013671875 0.076080322265625 -0.8442993164062446 +1.90225 0.490081787109375 0.076263427734375 -0.8442993164062446 +1.902375000000000256 0.4910888671875 0.076416015625 -0.8442993164062446 +1.9025 0.4910888671875 0.076416015625 -0.8442993164062446 +1.902625 0.492034912109375 0.076568603515625 -0.8442993164062446 +1.90275 0.492034912109375 0.076568603515625 -0.8442993164062446 +1.902875 0.492950439453125 0.076690673828125 -0.8442993164062446 +1.903 0.4937744140625 0.076812744140625 -0.8442993164062446 +1.903125 0.4937744140625 0.076812744140625 -0.8442993164062446 +1.90325 0.49456787109375 0.07696533203125 -0.8442993164062446 +1.903375 0.49456787109375 0.07696533203125 -0.8442993164062446 +1.9035 0.49530029296875 0.077056884765625 -0.8442993164062446 +1.90362500000000016 0.496002197265625 0.077178955078125 -0.8442993164062446 +1.90375 0.496002197265625 0.077178955078125 -0.8442993164062446 +1.903875 0.496612548828125 0.0772705078125 -0.8442993164062446 +1.90400000000000016 0.496612548828125 0.0772705078125 -0.8442993164062446 +1.904125 0.4971923828125 0.077362060546875 -0.8442993164062446 +1.90425 0.497711181640625 0.07745361328125 -0.8442993164062446 +1.904375 0.497711181640625 0.07745361328125 -0.8442993164062446 +1.9045 0.498199462890625 0.0775146484375 -0.8442993164062446 +1.904625 0.498199462890625 0.0775146484375 -0.8442993164062446 +1.90475 0.49859619140625 0.07757568359375 -0.8442993164062446 +1.904875 0.49896240234375 0.07763671875 -0.8442993164062446 +1.905 0.49896240234375 0.07763671875 -0.8442993164062446 +1.905125 0.499267578125 0.077667236328125 -0.8442993164062446 +1.90525 0.499267578125 0.077667236328125 -0.8442993164062446 +1.905375 0.49951171875 0.077728271484375 -0.8442993164062446 +1.9055 0.49969482421875 0.0777587890625 -0.8442993164062446 +1.90562500000000016 0.49969482421875 0.0777587890625 -0.8442993164062446 +1.90575 0.49981689453125 0.0777587890625 -0.8442993164062446 +1.905875 0.49981689453125 0.0777587890625 -0.8442993164062446 +1.90600000000000016 0.499908447265625 0.077789306640625 -0.8442993164062446 +1.906125 0.49993896484375 0.077789306640625 -0.8442993164062446 +1.90625 0.49993896484375 0.077789306640625 -0.8442993164062446 +1.906375 0.499908447265625 0.077789306640625 -0.8442993164062446 +1.9065 0.499908447265625 0.077789306640625 -0.8442993164062446 +1.906625 0.49981689453125 0.0777587890625 -0.8442993164062446 +1.90675 0.49969482421875 0.0777587890625 -0.8442993164062446 +1.906875 0.49969482421875 0.0777587890625 -0.8442993164062446 +1.907 0.49951171875 0.077728271484375 -0.8442993164062446 +1.907125 0.49951171875 0.077728271484375 -0.8442993164062446 +1.90725000000000016 0.499267578125 0.077667236328125 -0.8442993164062446 +1.907375 0.49896240234375 0.07763671875 -0.8442993164062446 +1.9075 0.49896240234375 0.07763671875 -0.8442993164062446 +1.90762500000000016 0.49859619140625 0.07757568359375 -0.8442993164062446 +1.90775 0.49859619140625 0.07757568359375 -0.8442993164062446 +1.907875 0.498199462890625 0.0775146484375 -0.8442993164062446 +1.90800000000000016 0.497711181640625 0.07745361328125 -0.8442993164062446 +1.908125 0.497711181640625 0.07745361328125 -0.8442993164062446 +1.90825 0.4971923828125 0.077362060546875 -0.8442993164062446 +1.908375 0.4971923828125 0.077362060546875 -0.8442993164062446 +1.9085 0.496612548828125 0.0772705078125 -0.8442993164062446 +1.908625 0.496002197265625 0.077178955078125 -0.8442993164062446 +1.90875 0.496002197265625 0.077178955078125 -0.8442993164062446 +1.908875 0.49530029296875 0.077056884765625 -0.8442993164062446 +1.909 0.49530029296875 0.077056884765625 -0.8442993164062446 +1.909125 0.49456787109375 0.07696533203125 -0.8442993164062446 +1.90925000000000016 0.4937744140625 0.076812744140625 -0.8442993164062446 +1.909375 0.4937744140625 0.076812744140625 -0.8442993164062446 +1.9095 0.492950439453125 0.076690673828125 -0.8442993164062446 +1.90962500000000016 0.492950439453125 0.076690673828125 -0.8442993164062446 +1.90975 0.492034912109375 0.076568603515625 -0.8442993164062446 +1.909875 0.4910888671875 0.076416015625 -0.8442993164062446 +1.91000000000000016 0.4910888671875 0.076416015625 -0.8442993164062446 +1.910125 0.490081787109375 0.076263427734375 -0.8442993164062446 +1.91025 0.490081787109375 0.076263427734375 -0.8442993164062446 +1.910375 0.489013671875 0.076080322265625 -0.8442993164062446 +1.9105 0.487884521484375 0.075897216796875 -0.8442993164062446 +1.910625 0.487884521484375 0.075897216796875 -0.8442993164062446 +1.91075 0.486724853515625 0.07574462890625 -0.8442993164062446 +1.910875 0.486724853515625 0.07574462890625 -0.8442993164062446 +1.911 0.485504150390625 0.075531005859375 -0.8442993164062446 +1.911125 0.484222412109375 0.075347900390625 -0.8442993164062446 +1.91125000000000016 0.484222412109375 0.075347900390625 -0.8442993164062446 +1.911375 0.48291015625 0.07513427734375 -0.8442993164062446 +1.9115 0.48291015625 0.07513427734375 -0.8442993164062446 +1.91162500000000016 0.48150634765625 0.074920654296875 -0.8442993164062446 +1.91175 0.480072021484375 0.07470703125 -0.8442993164062446 +1.911875 0.480072021484375 0.07470703125 -0.8442993164062446 +1.91200000000000016 0.478607177734375 0.074462890625 -0.8442993164062446 +1.912125 0.478607177734375 0.074462890625 -0.8442993164062446 +1.91225 0.47705078125 0.07421875 -0.8442993164062446 +1.912375 0.4754638671875 0.073974609375 -0.8442993164062446 +1.9125 0.4754638671875 0.073974609375 -0.8442993164062446 +1.912625 0.47381591796875 0.07373046875 -0.8442993164062446 +1.91275 0.47381591796875 0.07373046875 -0.8442993164062446 +1.912875 0.472137451171875 0.073455810546875 -0.8442993164062446 +1.913 0.470367431640625 0.07318115234375 -0.8442993164062446 +1.913125 0.470367431640625 0.07318115234375 -0.8442993164062446 +1.91325000000000016 0.46856689453125 0.072906494140625 -0.8442993164062446 +1.913375 0.46856689453125 0.072906494140625 -0.8442993164062446 +1.9135 0.46673583984375 0.0726318359375 -0.8442993164062446 +1.91362500000000016 0.464813232421875 0.07232666015625 -0.8442993164062446 +1.91375 0.464813232421875 0.07232666015625 -0.8442993164062446 +1.913875 0.462890625 0.072021484375 -0.8442993164062446 +1.914000000000000256 0.462890625 0.072021484375 -0.8442993164062446 +1.914125 0.46087646484375 0.07171630859375 -0.8442993164062446 +1.91425 0.458831787109375 0.071380615234375 -0.8442993164062446 +1.914375 0.458831787109375 0.071380615234375 -0.8442993164062446 +1.9145 0.45672607421875 0.071075439453125 -0.8442993164062446 +1.914625 0.45672607421875 0.071075439453125 -0.8442993164062446 +1.91475 0.454559326171875 0.070709228515625 -0.8442993164062446 +1.914875 0.452362060546875 0.07037353515625 -0.8442993164062446 +1.915 0.452362060546875 0.07037353515625 -0.8442993164062446 +1.915125 0.450103759765625 0.070037841796875 -0.8442993164062446 +1.91525000000000016 0.450103759765625 0.070037841796875 -0.8442993164062446 +1.915375 0.447784423828125 0.069671630859375 -0.8442993164062446 +1.9155 0.4454345703125 0.069305419921875 -0.8442993164062446 +1.91562500000000016 0.4454345703125 0.069305419921875 -0.8442993164062446 +1.91575 0.44305419921875 0.068939208984375 -0.8442993164062446 +1.915875 0.44305419921875 0.068939208984375 -0.8442993164062446 +1.916000000000000256 0.440582275390625 0.06854248046875 -0.8442993164062446 +1.916125 0.438079833984375 0.06817626953125 -0.8442993164062446 +1.91625 0.438079833984375 0.06817626953125 -0.8442993164062446 +1.916375 0.435546875 0.067779541015625 -0.8442993164062446 +1.9165 0.435546875 0.067779541015625 -0.8442993164062446 +1.916625 0.432952880859375 0.067352294921875 -0.8442993164062446 +1.91675 0.4302978515625 0.06695556640625 -0.8442993164062446 +1.916875 0.4302978515625 0.06695556640625 -0.8442993164062446 +1.917 0.4276123046875 0.0665283203125 -0.8442993164062446 +1.917125 0.4276123046875 0.0665283203125 -0.8442993164062446 +1.91725000000000016 0.424896240234375 0.06610107421875 -0.8442993164062446 +1.917375 0.422119140625 0.065673828125 -0.8442993164062446 +1.9175 0.422119140625 0.065673828125 -0.8442993164062446 +1.91762500000000016 0.419281005859375 0.06524658203125 -0.8442993164062446 +1.91775 0.419281005859375 0.06524658203125 -0.8442993164062446 +1.917875 0.416412353515625 0.064788818359375 -0.8442993164062446 +1.918000000000000256 0.413482666015625 0.0643310546875 -0.8442993164062446 +1.918125 0.413482666015625 0.0643310546875 -0.8442993164062446 +1.91825 0.4105224609375 0.063873291015625 -0.8442993164062446 +1.918375 0.4105224609375 0.063873291015625 -0.8442993164062446 +1.9185 0.407501220703125 0.06341552734375 -0.8442993164062446 +1.918625 0.404449462890625 0.06292724609375 -0.8442993164062446 +1.91875 0.404449462890625 0.06292724609375 -0.8442993164062446 +1.918875 0.401336669921875 0.06243896484375 -0.8442993164062446 +1.919 0.401336669921875 0.06243896484375 -0.8442993164062446 +1.919125 0.398193359375 0.06195068359375 -0.8442993164062446 +1.91925000000000016 0.39501953125 0.06146240234375 -0.8442993164062446 +1.919375 0.39501953125 0.06146240234375 -0.8442993164062446 +1.9195 0.39178466796875 0.060943603515625 -0.8442993164062446 +1.91962500000000016 0.39178466796875 0.060943603515625 -0.8442993164062446 +1.91975 0.388519287109375 0.060455322265625 -0.8442993164062446 +1.919875 0.38519287109375 0.0599365234375 -0.8442993164062446 +1.92 0.38519287109375 0.166656494140625 -0.5672241210937414 +1.920125 0.3818359375 0.16522216796875 -0.5672241210937414 +1.92025 0.3818359375 0.16522216796875 -0.5672241210937414 +1.920375 0.378448486328125 0.16375732421875 -0.5672241210937414 +1.9205 0.375 0.162261962890625 -0.5672241210937414 +1.920625 0.375 0.162261962890625 -0.5672241210937414 +1.92075 0.37152099609375 0.160736083984375 -0.5672241210937414 +1.920875 0.37152099609375 0.160736083984375 -0.5672241210937414 +1.921 0.36798095703125 0.159210205078125 -0.5672241210937414 +1.921125 0.36444091796875 0.157684326171875 -0.5672241210937414 +1.92125000000000016 0.36444091796875 0.157684326171875 -0.5672241210937414 +1.921375 0.360809326171875 0.1561279296875 -0.5672241210937414 +1.9215 0.360809326171875 0.1561279296875 -0.5672241210937414 +1.92162500000000016 0.357177734375 0.154541015625 -0.5672241210937414 +1.92175 0.353485107421875 0.1529541015625 -0.5672241210937414 +1.921875 0.353485107421875 0.1529541015625 -0.5672241210937414 +1.922 0.349761962890625 0.151336669921875 -0.5672241210937414 +1.922125 0.349761962890625 0.151336669921875 -0.5672241210937414 +1.92225 0.34600830078125 0.14971923828125 -0.5672241210937414 +1.922375 0.34222412109375 0.1480712890625 -0.5672241210937414 +1.9225 0.34222412109375 0.1480712890625 -0.5672241210937414 +1.922625 0.33837890625 0.14642333984375 -0.5672241210937414 +1.92275 0.33837890625 0.14642333984375 -0.5672241210937414 +1.92287500000000016 0.334503173828125 0.144744873046875 -0.5672241210937414 +1.923 0.330596923828125 0.143035888671875 -0.5672241210937414 +1.923125 0.330596923828125 0.143035888671875 -0.5672241210937414 +1.92325000000000016 0.32666015625 0.141326904296875 -0.5672241210937414 +1.923375 0.32666015625 0.141326904296875 -0.5672241210937414 +1.9235 0.322662353515625 0.139617919921875 -0.5672241210937414 +1.92362500000000016 0.31866455078125 0.13787841796875 -0.5672241210937414 +1.92375 0.31866455078125 0.13787841796875 -0.5672241210937414 +1.923875 0.314605712890625 0.1361083984375 -0.5672241210937414 +1.924 0.314605712890625 0.1361083984375 -0.5672241210937414 +1.924125 0.310516357421875 0.134368896484375 -0.5672241210937414 +1.92425 0.306396484375 0.132568359375 -0.5672241210937414 +1.924375 0.306396484375 0.132568359375 -0.5672241210937414 +1.9245 0.30224609375 0.130767822265625 -0.5672241210937414 +1.924625 0.30224609375 0.130767822265625 -0.5672241210937414 +1.92475 0.298065185546875 0.12896728515625 -0.5672241210937414 +1.92487500000000016 0.2938232421875 0.12713623046875 -0.5672241210937414 +1.925 0.2938232421875 0.12713623046875 -0.5672241210937414 +1.925125 0.289581298828125 0.12530517578125 -0.5672241210937414 +1.92525000000000016 0.289581298828125 0.12530517578125 -0.5672241210937414 +1.925375 0.285308837890625 0.123443603515625 -0.5672241210937414 +1.9255 0.280975341796875 0.12158203125 -0.5672241210937414 +1.92562500000000016 0.280975341796875 0.12158203125 -0.5672241210937414 +1.92575 0.276641845703125 0.11968994140625 -0.5672241210937414 +1.925875 0.276641845703125 0.11968994140625 -0.5672241210937414 +1.926 0.27227783203125 0.1177978515625 -0.5672241210937414 +1.926125 0.267852783203125 0.11590576171875 -0.5672241210937414 +1.92625 0.267852783203125 0.11590576171875 -0.5672241210937414 +1.926375 0.263427734375 0.113983154296875 -0.5672241210937414 +1.9265 0.263427734375 0.113983154296875 -0.5672241210937414 +1.926625 0.25897216796875 0.112060546875 -0.5672241210937414 +1.92675 0.25445556640625 0.110107421875 -0.5672241210937414 +1.92687500000000016 0.25445556640625 0.110107421875 -0.5672241210937414 +1.927 0.249969482421875 0.108154296875 -0.5672241210937414 +1.927125 0.249969482421875 0.108154296875 -0.5672241210937414 +1.92725000000000016 0.24542236328125 0.106170654296875 -0.5672241210937414 +1.927375 0.2408447265625 0.10418701171875 -0.5672241210937414 +1.9275 0.2408447265625 0.10418701171875 -0.5672241210937414 +1.92762500000000016 0.236236572265625 0.102203369140625 -0.5672241210937414 +1.92775 0.236236572265625 0.102203369140625 -0.5672241210937414 +1.927875 0.231597900390625 0.1002197265625 -0.5672241210937414 +1.928 0.226959228515625 0.09820556640625 -0.5672241210937414 +1.928125 0.226959228515625 0.09820556640625 -0.5672241210937414 +1.92825 0.2222900390625 0.096160888671875 -0.5672241210937414 +1.928375 0.2222900390625 0.096160888671875 -0.5672241210937414 +1.9285 0.21759033203125 0.094146728515625 -0.5672241210937414 +1.928625 0.212860107421875 0.09210205078125 -0.5672241210937414 +1.92875 0.212860107421875 0.09210205078125 -0.5672241210937414 +1.92887500000000016 0.208099365234375 0.09002685546875 -0.5672241210937414 +1.929 0.208099365234375 0.09002685546875 -0.5672241210937414 +1.929125 0.203338623046875 0.087982177734375 -0.5672241210937414 +1.92925000000000016 0.19854736328125 0.085906982421875 -0.5672241210937414 +1.929375 0.19854736328125 0.085906982421875 -0.5672241210937414 +1.9295 0.1937255859375 0.08380126953125 -0.5672241210937414 +1.929625000000000256 0.1937255859375 0.08380126953125 -0.5672241210937414 +1.92975 0.188873291015625 0.08172607421875 -0.5672241210937414 +1.929875 0.18402099609375 0.079620361328125 -0.5672241210937414 +1.93 0.18402099609375 0.079620361328125 -0.5672241210937414 +1.930125 0.17913818359375 0.0775146484375 -0.5672241210937414 +1.93025 0.17913818359375 0.0775146484375 -0.5672241210937414 +1.930375 0.17425537109375 0.07537841796875 -0.5672241210937414 +1.9305 0.169342041015625 0.073272705078125 -0.5672241210937414 +1.930625 0.169342041015625 0.073272705078125 -0.5672241210937414 +1.93075 0.164398193359375 0.071136474609375 -0.5672241210937414 +1.93087500000000016 0.164398193359375 0.071136474609375 -0.5672241210937414 +1.931 0.159454345703125 0.0689697265625 -0.5672241210937414 +1.931125 0.15447998046875 0.06683349609375 -0.5672241210937414 +1.93125000000000016 0.15447998046875 0.06683349609375 -0.5672241210937414 +1.931375 0.14947509765625 0.064666748046875 -0.5672241210937414 +1.9315 0.14947509765625 0.064666748046875 -0.5672241210937414 +1.931625000000000256 0.14447021484375 0.0625 -0.5672241210937414 +1.93175 0.13946533203125 0.060333251953125 -0.5672241210937414 +1.931875 0.13946533203125 0.060333251953125 -0.5672241210937414 +1.932 0.134429931640625 0.05816650390625 -0.5672241210937414 +1.932125 0.134429931640625 0.05816650390625 -0.5672241210937414 +1.93225 0.129364013671875 0.05596923828125 -0.5672241210937414 +1.932375 0.124298095703125 0.05377197265625 -0.5672241210937414 +1.9325 0.124298095703125 0.05377197265625 -0.5672241210937414 +1.932625 0.119232177734375 0.05157470703125 -0.5672241210937414 +1.93275 0.119232177734375 0.05157470703125 -0.5672241210937414 +1.93287500000000016 0.1141357421875 0.04937744140625 -0.5672241210937414 +1.933 0.109039306640625 0.04718017578125 -0.5672241210937414 +1.933125 0.109039306640625 0.04718017578125 -0.5672241210937414 +1.93325000000000016 0.103912353515625 0.044952392578125 -0.5672241210937414 +1.933375 0.103912353515625 0.044952392578125 -0.5672241210937414 +1.9335 0.098785400390625 0.042755126953125 -0.5672241210937414 +1.933625000000000256 0.093658447265625 0.04052734375 -0.5672241210937414 +1.93375 0.093658447265625 0.04052734375 -0.5672241210937414 +1.933875 0.0885009765625 0.038299560546875 -0.5672241210937414 +1.934 0.0885009765625 0.038299560546875 -0.5672241210937414 +1.934125 0.083343505859375 0.03607177734375 -0.5672241210937414 +1.93425 0.07818603515625 0.0338134765625 -0.5672241210937414 +1.934375 0.07818603515625 0.0338134765625 -0.5672241210937414 +1.9345 0.072998046875 0.031585693359375 -0.5672241210937414 +1.934625 0.072998046875 0.031585693359375 -0.5672241210937414 +1.93475 0.06781005859375 0.029327392578125 -0.5672241210937414 +1.93487500000000016 0.0626220703125 0.027099609375 -0.5672241210937414 +1.935 0.0626220703125 0.027099609375 -0.5672241210937414 +1.935125 0.05743408203125 0.02484130859375 -0.5672241210937414 +1.93525000000000016 0.05743408203125 0.02484130859375 -0.5672241210937414 +1.935375 0.05224609375 0.0225830078125 -0.5672241210937414 +1.9355 0.047027587890625 0.02032470703125 -0.5672241210937414 +1.935625000000000256 0.047027587890625 0.02032470703125 -0.5672241210937414 +1.93575 0.04180908203125 0.01806640625 -0.5672241210937414 +1.935875 0.04180908203125 0.01806640625 -0.5672241210937414 +1.936 0.036590576171875 0.01580810546875 -0.5672241210937414 +1.936125 0.0313720703125 0.013580322265625 -0.5672241210937414 +1.93625 0.0313720703125 0.013580322265625 -0.5672241210937414 +1.936375 0.026123046875 0.01129150390625 -0.5672241210937414 +1.9365 0.026123046875 0.01129150390625 -0.5672241210937414 +1.936625 0.020904541015625 0.009033203125 -0.5672241210937414 +1.93675 0.01568603515625 0.00677490234375 -0.5672241210937414 +1.93687500000000016 0.01568603515625 0.00677490234375 -0.5672241210937414 +1.937 0.01043701171875 0.0045166015625 -0.5672241210937414 +1.937125 0.01043701171875 0.0045166015625 -0.5672241210937414 +1.93725000000000016 0.005218505859375 0.00225830078125 -0.5672241210937414 +1.937375 0.0 0.0 -0.5672241210937414 +1.9375 0.0 0.0 -0.5672241210937414 +1.937625 -0.005218505859375 -0.00225830078125 -0.5672241210937414 +1.93775 -0.005218505859375 -0.00225830078125 -0.5672241210937414 +1.937875 -0.01043701171875 -0.0045166015625 -0.5672241210937414 +1.938 -0.01568603515625 -0.00677490234375 -0.5672241210937414 +1.938125 -0.01568603515625 -0.00677490234375 -0.5672241210937414 +1.93825 -0.020904541015625 -0.009033203125 -0.5672241210937414 +1.938375 -0.020904541015625 -0.009033203125 -0.5672241210937414 +1.93850000000000016 -0.026123046875 -0.01129150390625 -0.5672241210937414 +1.938625 -0.0313720703125 -0.013580322265625 -0.5672241210937414 +1.93875 -0.0313720703125 -0.013580322265625 -0.5672241210937414 +1.93887500000000016 -0.036590576171875 -0.01580810546875 -0.5672241210937414 +1.939 -0.036590576171875 -0.01580810546875 -0.5672241210937414 +1.939125 -0.04180908203125 -0.01806640625 -0.5672241210937414 +1.93925000000000016 -0.047027587890625 -0.02032470703125 -0.5672241210937414 +1.939375 -0.047027587890625 -0.02032470703125 -0.5672241210937414 +1.9395 -0.05224609375 -0.0225830078125 -0.5672241210937414 +1.939625 -0.05224609375 -0.0225830078125 -0.5672241210937414 +1.93975 -0.05743408203125 -0.02484130859375 -0.5672241210937414 +1.939875 -0.0626220703125 -0.027099609375 -0.5672241210937414 +1.94 -0.0626220703125 -0.027099609375 -0.5672241210937414 +1.940125 -0.06781005859375 -0.029327392578125 -0.5672241210937414 +1.94025 -0.06781005859375 -0.029327392578125 -0.5672241210937414 +1.940375 -0.072998046875 -0.031585693359375 -0.5672241210937414 +1.94050000000000016 -0.07818603515625 -0.0338134765625 -0.5672241210937414 +1.940625 -0.07818603515625 -0.0338134765625 -0.5672241210937414 +1.94075 -0.083343505859375 -0.03607177734375 -0.5672241210937414 +1.94087500000000016 -0.083343505859375 -0.03607177734375 -0.5672241210937414 +1.941 -0.0885009765625 -0.038299560546875 -0.5672241210937414 +1.941125 -0.093658447265625 -0.04052734375 -0.5672241210937414 +1.94125000000000016 -0.093658447265625 -0.04052734375 -0.5672241210937414 +1.941375 -0.098785400390625 -0.042755126953125 -0.5672241210937414 +1.9415 -0.098785400390625 -0.042755126953125 -0.5672241210937414 +1.941625 -0.103912353515625 -0.044952392578125 -0.5672241210937414 +1.94175 -0.109039306640625 -0.04718017578125 -0.5672241210937414 +1.941875 -0.109039306640625 -0.04718017578125 -0.5672241210937414 +1.942 -0.1141357421875 -0.04937744140625 -0.5672241210937414 +1.942125 -0.1141357421875 -0.04937744140625 -0.5672241210937414 +1.94225 -0.119232177734375 -0.05157470703125 -0.5672241210937414 +1.942375 -0.124298095703125 -0.05377197265625 -0.5672241210937414 +1.94250000000000016 -0.124298095703125 -0.05377197265625 -0.5672241210937414 +1.942625 -0.129364013671875 -0.05596923828125 -0.5672241210937414 +1.94275 -0.129364013671875 -0.05596923828125 -0.5672241210937414 +1.94287500000000016 -0.134429931640625 -0.05816650390625 -0.5672241210937414 +1.943 -0.13946533203125 -0.060333251953125 -0.5672241210937414 +1.943125 -0.13946533203125 -0.060333251953125 -0.5672241210937414 +1.94325000000000016 -0.14447021484375 -0.0625 -0.5672241210937414 +1.943375 -0.14447021484375 -0.0625 -0.5672241210937414 +1.9435 -0.14947509765625 -0.064666748046875 -0.5672241210937414 +1.943625 -0.15447998046875 -0.06683349609375 -0.5672241210937414 +1.94375 -0.15447998046875 -0.06683349609375 -0.5672241210937414 +1.943875 -0.159454345703125 -0.0689697265625 -0.5672241210937414 +1.944 -0.159454345703125 -0.0689697265625 -0.5672241210937414 +1.944125 -0.164398193359375 -0.071136474609375 -0.5672241210937414 +1.94425 -0.169342041015625 -0.073272705078125 -0.5672241210937414 +1.944375 -0.169342041015625 -0.073272705078125 -0.5672241210937414 +1.94450000000000016 -0.17425537109375 -0.07537841796875 -0.5672241210937414 +1.944625 -0.17425537109375 -0.07537841796875 -0.5672241210937414 +1.94475 -0.17913818359375 -0.0775146484375 -0.5672241210937414 +1.94487500000000016 -0.18402099609375 -0.079620361328125 -0.5672241210937414 +1.945 -0.18402099609375 -0.079620361328125 -0.5672241210937414 +1.945125 -0.188873291015625 -0.08172607421875 -0.5672241210937414 +1.945250000000000256 -0.188873291015625 -0.08172607421875 -0.5672241210937414 +1.945375 -0.1937255859375 -0.08380126953125 -0.5672241210937414 +1.9455 -0.19854736328125 -0.085906982421875 -0.5672241210937414 +1.945625 -0.19854736328125 -0.085906982421875 -0.5672241210937414 +1.94575 -0.203338623046875 -0.087982177734375 -0.5672241210937414 +1.945875 -0.203338623046875 -0.087982177734375 -0.5672241210937414 +1.946 -0.208099365234375 -0.09002685546875 -0.5672241210937414 +1.946125 -0.212860107421875 -0.09210205078125 -0.5672241210937414 +1.94625 -0.212860107421875 -0.09210205078125 -0.5672241210937414 +1.946375 -0.21759033203125 -0.094146728515625 -0.5672241210937414 +1.94650000000000016 -0.21759033203125 -0.094146728515625 -0.5672241210937414 +1.946625 -0.2222900390625 -0.096160888671875 -0.5672241210937414 +1.94675 -0.226959228515625 -0.09820556640625 -0.5672241210937414 +1.94687500000000016 -0.226959228515625 -0.09820556640625 -0.5672241210937414 +1.947 -0.231597900390625 -0.1002197265625 -0.5672241210937414 +1.947125 -0.231597900390625 -0.1002197265625 -0.5672241210937414 +1.947250000000000256 -0.236236572265625 -0.102203369140625 -0.5672241210937414 +1.947375 -0.2408447265625 -0.10418701171875 -0.5672241210937414 +1.9475 -0.2408447265625 -0.10418701171875 -0.5672241210937414 +1.947625 -0.24542236328125 -0.106170654296875 -0.5672241210937414 +1.94775 -0.24542236328125 -0.106170654296875 -0.5672241210937414 +1.947875 -0.249969482421875 -0.108154296875 -0.5672241210937414 +1.948 -0.25445556640625 -0.110107421875 -0.5672241210937414 +1.948125 -0.25445556640625 -0.110107421875 -0.5672241210937414 +1.94825 -0.25897216796875 -0.112060546875 -0.5672241210937414 +1.948375 -0.25897216796875 -0.112060546875 -0.5672241210937414 +1.94850000000000016 -0.263427734375 -0.113983154296875 -0.5672241210937414 +1.948625 -0.267852783203125 -0.11590576171875 -0.5672241210937414 +1.94875 -0.267852783203125 -0.11590576171875 -0.5672241210937414 +1.94887500000000016 -0.27227783203125 -0.1177978515625 -0.5672241210937414 +1.949 -0.27227783203125 -0.1177978515625 -0.5672241210937414 +1.949125 -0.276641845703125 -0.11968994140625 -0.5672241210937414 +1.949250000000000256 -0.280975341796875 -0.12158203125 -0.5672241210937414 +1.949375 -0.280975341796875 -0.12158203125 -0.5672241210937414 +1.9495 -0.285308837890625 -0.123443603515625 -0.5672241210937414 +1.949625 -0.285308837890625 -0.123443603515625 -0.5672241210937414 +1.94975 -0.289581298828125 -0.12530517578125 -0.5672241210937414 +1.949875 -0.2938232421875 -0.12713623046875 -0.5672241210937414 +1.95 -0.2938232421875 -0.12713623046875 -0.5672241210937414 +1.950125 -0.298065185546875 -0.12896728515625 -0.5672241210937414 +1.95025 -0.298065185546875 -0.12896728515625 -0.5672241210937414 +1.950375 -0.30224609375 -0.130767822265625 -0.5672241210937414 +1.95050000000000016 -0.306396484375 -0.132568359375 -0.5672241210937414 +1.950625 -0.306396484375 -0.132568359375 -0.5672241210937414 +1.95075 -0.310516357421875 -0.134368896484375 -0.5672241210937414 +1.95087500000000016 -0.310516357421875 -0.134368896484375 -0.5672241210937414 +1.951 -0.314605712890625 -0.1361083984375 -0.5672241210937414 +1.951125 -0.31866455078125 -0.13787841796875 -0.5672241210937414 +1.951250000000000256 -0.31866455078125 -0.13787841796875 -0.5672241210937414 +1.951375 -0.322662353515625 -0.139617919921875 -0.5672241210937414 +1.9515 -0.322662353515625 -0.139617919921875 -0.5672241210937414 +1.951625 -0.32666015625 -0.141326904296875 -0.5672241210937414 +1.95175 -0.330596923828125 -0.143035888671875 -0.5672241210937414 +1.951875 -0.330596923828125 -0.143035888671875 -0.5672241210937414 +1.952 -0.334503173828125 -0.2677001953125 -0.1996826171874888 +1.952125 -0.334503173828125 -0.2677001953125 -0.1996826171874888 +1.95225 -0.33837890625 -0.27081298828125 -0.1996826171874888 +1.952375 -0.34222412109375 -0.27386474609375 -0.1996826171874888 +1.95250000000000016 -0.34222412109375 -0.27386474609375 -0.1996826171874888 +1.952625 -0.34600830078125 -0.27691650390625 -0.1996826171874888 +1.95275 -0.34600830078125 -0.27691650390625 -0.1996826171874888 +1.95287500000000016 -0.349761962890625 -0.2799072265625 -0.1996826171874888 +1.953 -0.353485107421875 -0.28289794921875 -0.1996826171874888 +1.953125 -0.353485107421875 -0.28289794921875 -0.1996826171874888 +1.95325 -0.357177734375 -0.285858154296875 -0.1996826171874888 +1.953375 -0.357177734375 -0.285858154296875 -0.1996826171874888 +1.9535 -0.360809326171875 -0.28875732421875 -0.1996826171874888 +1.953625 -0.36444091796875 -0.291656494140625 -0.1996826171874888 +1.95375 -0.36444091796875 -0.291656494140625 -0.1996826171874888 +1.953875 -0.36798095703125 -0.29449462890625 -0.1996826171874888 +1.954 -0.36798095703125 -0.29449462890625 -0.1996826171874888 +1.95412500000000016 -0.37152099609375 -0.29730224609375 -0.1996826171874888 +1.95425 -0.375 -0.30010986328125 -0.1996826171874888 +1.954375 -0.375 -0.30010986328125 -0.1996826171874888 +1.95450000000000016 -0.378448486328125 -0.3028564453125 -0.1996826171874888 +1.954625 -0.378448486328125 -0.3028564453125 -0.1996826171874888 +1.95475 -0.3818359375 -0.305572509765625 -0.1996826171874888 +1.95487500000000016 -0.38519287109375 -0.308258056640625 -0.1996826171874888 +1.955 -0.38519287109375 -0.308258056640625 -0.1996826171874888 +1.955125 -0.388519287109375 -0.3109130859375 -0.1996826171874888 +1.95525 -0.388519287109375 -0.3109130859375 -0.1996826171874888 +1.955375 -0.39178466796875 -0.31353759765625 -0.1996826171874888 +1.9555 -0.39501953125 -0.316131591796875 -0.1996826171874888 +1.955625 -0.39501953125 -0.316131591796875 -0.1996826171874888 +1.95575 -0.398193359375 -0.31866455078125 -0.1996826171874888 +1.955875 -0.398193359375 -0.31866455078125 -0.1996826171874888 +1.956 -0.401336669921875 -0.321197509765625 -0.1996826171874888 +1.95612500000000016 -0.404449462890625 -0.32366943359375 -0.1996826171874888 +1.95625 -0.404449462890625 -0.32366943359375 -0.1996826171874888 +1.956375 -0.407501220703125 -0.32611083984375 -0.1996826171874888 +1.95650000000000016 -0.407501220703125 -0.32611083984375 -0.1996826171874888 +1.956625 -0.4105224609375 -0.328521728515625 -0.1996826171874888 +1.95675 -0.413482666015625 -0.330902099609375 -0.1996826171874888 +1.95687500000000016 -0.413482666015625 -0.330902099609375 -0.1996826171874888 +1.957 -0.416412353515625 -0.333221435546875 -0.1996826171874888 +1.957125 -0.416412353515625 -0.333221435546875 -0.1996826171874888 +1.95725 -0.419281005859375 -0.335540771484375 -0.1996826171874888 +1.957375 -0.422119140625 -0.337799072265625 -0.1996826171874888 +1.9575 -0.422119140625 -0.337799072265625 -0.1996826171874888 +1.957625 -0.424896240234375 -0.34002685546875 -0.1996826171874888 +1.95775 -0.424896240234375 -0.34002685546875 -0.1996826171874888 +1.957875 -0.4276123046875 -0.34222412109375 -0.1996826171874888 +1.958 -0.4302978515625 -0.3443603515625 -0.1996826171874888 +1.95812500000000016 -0.4302978515625 -0.3443603515625 -0.1996826171874888 +1.95825 -0.432952880859375 -0.34649658203125 -0.1996826171874888 +1.958375 -0.432952880859375 -0.34649658203125 -0.1996826171874888 +1.95850000000000016 -0.435546875 -0.34857177734375 -0.1996826171874888 +1.958625 -0.438079833984375 -0.3505859375 -0.1996826171874888 +1.95875 -0.438079833984375 -0.3505859375 -0.1996826171874888 +1.95887500000000016 -0.440582275390625 -0.35260009765625 -0.1996826171874888 +1.959 -0.440582275390625 -0.35260009765625 -0.1996826171874888 +1.959125 -0.44305419921875 -0.35455322265625 -0.1996826171874888 +1.95925 -0.4454345703125 -0.356475830078125 -0.1996826171874888 +1.959375 -0.4454345703125 -0.356475830078125 -0.1996826171874888 +1.9595 -0.447784423828125 -0.358367919921875 -0.1996826171874888 +1.959625 -0.447784423828125 -0.358367919921875 -0.1996826171874888 +1.95975 -0.450103759765625 -0.360198974609375 -0.1996826171874888 +1.959875 -0.452362060546875 -0.36199951171875 -0.1996826171874888 +1.96 -0.452362060546875 -0.36199951171875 -0.1996826171874888 +1.96012500000000016 -0.454559326171875 -0.36376953125 -0.1996826171874888 +1.96025 -0.454559326171875 -0.36376953125 -0.1996826171874888 +1.960375 -0.45672607421875 -0.365509033203125 -0.1996826171874888 +1.96050000000000016 -0.458831787109375 -0.3671875 -0.1996826171874888 +1.960625 -0.458831787109375 -0.3671875 -0.1996826171874888 +1.96075 -0.46087646484375 -0.368804931640625 -0.1996826171874888 +1.960875000000000256 -0.46087646484375 -0.368804931640625 -0.1996826171874888 +1.961 -0.462890625 -0.37042236328125 -0.1996826171874888 +1.961125 -0.464813232421875 -0.371978759765625 -0.1996826171874888 +1.96125 -0.464813232421875 -0.371978759765625 -0.1996826171874888 +1.961375 -0.46673583984375 -0.373504638671875 -0.1996826171874888 +1.9615 -0.46673583984375 -0.373504638671875 -0.1996826171874888 +1.961625 -0.46856689453125 -0.374969482421875 -0.1996826171874888 +1.96175 -0.470367431640625 -0.376434326171875 -0.1996826171874888 +1.961875 -0.470367431640625 -0.376434326171875 -0.1996826171874888 +1.962 -0.472137451171875 -0.377838134765625 -0.1996826171874888 +1.96212500000000016 -0.472137451171875 -0.377838134765625 -0.1996826171874888 +1.96225 -0.47381591796875 -0.379180908203125 -0.1996826171874888 +1.962375 -0.4754638671875 -0.3804931640625 -0.1996826171874888 +1.96250000000000016 -0.4754638671875 -0.3804931640625 -0.1996826171874888 +1.962625 -0.47705078125 -0.38177490234375 -0.1996826171874888 +1.96275 -0.47705078125 -0.38177490234375 -0.1996826171874888 +1.962875000000000256 -0.478607177734375 -0.38299560546875 -0.1996826171874888 +1.963 -0.480072021484375 -0.384185791015625 -0.1996826171874888 +1.963125 -0.480072021484375 -0.384185791015625 -0.1996826171874888 +1.96325 -0.48150634765625 -0.385345458984375 -0.1996826171874888 +1.963375 -0.48150634765625 -0.385345458984375 -0.1996826171874888 +1.9635 -0.48291015625 -0.386444091796875 -0.1996826171874888 +1.963625 -0.484222412109375 -0.38751220703125 -0.1996826171874888 +1.96375 -0.484222412109375 -0.38751220703125 -0.1996826171874888 +1.963875 -0.485504150390625 -0.3885498046875 -0.1996826171874888 +1.964 -0.485504150390625 -0.3885498046875 -0.1996826171874888 +1.96412500000000016 -0.486724853515625 -0.3895263671875 -0.1996826171874888 +1.96425 -0.487884521484375 -0.39044189453125 -0.1996826171874888 +1.964375 -0.487884521484375 -0.39044189453125 -0.1996826171874888 +1.96450000000000016 -0.489013671875 -0.391326904296875 -0.1996826171874888 +1.964625 -0.489013671875 -0.391326904296875 -0.1996826171874888 +1.96475 -0.490081787109375 -0.392181396484375 -0.1996826171874888 +1.964875000000000256 -0.4910888671875 -0.39300537109375 -0.1996826171874888 +1.965 -0.4910888671875 -0.39300537109375 -0.1996826171874888 +1.965125 -0.492034912109375 -0.393768310546875 -0.1996826171874888 +1.96525 -0.492034912109375 -0.393768310546875 -0.1996826171874888 +1.965375 -0.492950439453125 -0.39447021484375 -0.1996826171874888 +1.9655 -0.4937744140625 -0.3951416015625 -0.1996826171874888 +1.965625 -0.4937744140625 -0.3951416015625 -0.1996826171874888 +1.96575 -0.49456787109375 -0.395782470703125 -0.1996826171874888 +1.965875 -0.49456787109375 -0.395782470703125 -0.1996826171874888 +1.966 -0.49530029296875 -0.3963623046875 -0.1996826171874888 +1.96612500000000016 -0.496002197265625 -0.39691162109375 -0.1996826171874888 +1.96625 -0.496002197265625 -0.39691162109375 -0.1996826171874888 +1.966375 -0.496612548828125 -0.397430419921875 -0.1996826171874888 +1.96650000000000016 -0.496612548828125 -0.397430419921875 -0.1996826171874888 +1.966625 -0.4971923828125 -0.39788818359375 -0.1996826171874888 +1.96675 -0.497711181640625 -0.3983154296875 -0.1996826171874888 +1.966875000000000256 -0.497711181640625 -0.3983154296875 -0.1996826171874888 +1.967 -0.498199462890625 -0.398681640625 -0.1996826171874888 +1.967125 -0.498199462890625 -0.398681640625 -0.1996826171874888 +1.96725 -0.49859619140625 -0.399017333984375 -0.1996826171874888 +1.967375 -0.49896240234375 -0.3992919921875 -0.1996826171874888 +1.9675 -0.49896240234375 -0.3992919921875 -0.1996826171874888 +1.967625 -0.499267578125 -0.3995361328125 -0.1996826171874888 +1.96775 -0.499267578125 -0.3995361328125 -0.1996826171874888 +1.967875 -0.49951171875 -0.39971923828125 -0.1996826171874888 +1.968 -0.49969482421875 -0.399871826171875 -0.1996826171874888 +1.96812500000000016 -0.49969482421875 -0.399871826171875 -0.1996826171874888 +1.96825 -0.49981689453125 -0.399993896484375 -0.1996826171874888 +1.968375 -0.49981689453125 -0.399993896484375 -0.1996826171874888 +1.96850000000000016 -0.499908447265625 -0.400054931640625 -0.1996826171874888 +1.968625 -0.49993896484375 -0.40008544921875 -0.1996826171874888 +1.96875 -0.49993896484375 -0.40008544921875 -0.1996826171874888 +1.968875 -0.499908447265625 -0.400054931640625 -0.1996826171874888 +1.969 -0.499908447265625 -0.400054931640625 -0.1996826171874888 +1.969125 -0.49981689453125 -0.399993896484375 -0.1996826171874888 +1.96925 -0.49969482421875 -0.399871826171875 -0.1996826171874888 +1.969375 -0.49969482421875 -0.399871826171875 -0.1996826171874888 +1.9695 -0.49951171875 -0.39971923828125 -0.1996826171874888 +1.969625 -0.49951171875 -0.39971923828125 -0.1996826171874888 +1.96975000000000016 -0.499267578125 -0.3995361328125 -0.1996826171874888 +1.969875 -0.49896240234375 -0.3992919921875 -0.1996826171874888 +1.97 -0.49896240234375 -0.3992919921875 -0.1996826171874888 +1.97012500000000016 -0.49859619140625 -0.399017333984375 -0.1996826171874888 +1.97025 -0.49859619140625 -0.399017333984375 -0.1996826171874888 +1.970375 -0.498199462890625 -0.398681640625 -0.1996826171874888 +1.97050000000000016 -0.497711181640625 -0.3983154296875 -0.1996826171874888 +1.970625 -0.497711181640625 -0.3983154296875 -0.1996826171874888 +1.97075 -0.4971923828125 -0.39788818359375 -0.1996826171874888 +1.970875 -0.4971923828125 -0.39788818359375 -0.1996826171874888 +1.971 -0.496612548828125 -0.397430419921875 -0.1996826171874888 +1.971125 -0.496002197265625 -0.39691162109375 -0.1996826171874888 +1.97125 -0.496002197265625 -0.39691162109375 -0.1996826171874888 +1.971375 -0.49530029296875 -0.3963623046875 -0.1996826171874888 +1.9715 -0.49530029296875 -0.3963623046875 -0.1996826171874888 +1.971625 -0.49456787109375 -0.395782470703125 -0.1996826171874888 +1.97175000000000016 -0.4937744140625 -0.3951416015625 -0.1996826171874888 +1.971875 -0.4937744140625 -0.3951416015625 -0.1996826171874888 +1.972 -0.492950439453125 -0.39447021484375 -0.1996826171874888 +1.97212500000000016 -0.492950439453125 -0.39447021484375 -0.1996826171874888 +1.97225 -0.492034912109375 -0.393768310546875 -0.1996826171874888 +1.972375 -0.4910888671875 -0.39300537109375 -0.1996826171874888 +1.97250000000000016 -0.4910888671875 -0.39300537109375 -0.1996826171874888 +1.972625 -0.490081787109375 -0.392181396484375 -0.1996826171874888 +1.97275 -0.490081787109375 -0.392181396484375 -0.1996826171874888 +1.972875 -0.489013671875 -0.391326904296875 -0.1996826171874888 +1.973 -0.487884521484375 -0.39044189453125 -0.1996826171874888 +1.973125 -0.487884521484375 -0.39044189453125 -0.1996826171874888 +1.97325 -0.486724853515625 -0.3895263671875 -0.1996826171874888 +1.973375 -0.486724853515625 -0.3895263671875 -0.1996826171874888 +1.9735 -0.485504150390625 -0.3885498046875 -0.1996826171874888 +1.973625 -0.484222412109375 -0.38751220703125 -0.1996826171874888 +1.97375000000000016 -0.484222412109375 -0.38751220703125 -0.1996826171874888 +1.973875 -0.48291015625 -0.386444091796875 -0.1996826171874888 +1.974 -0.48291015625 -0.386444091796875 -0.1996826171874888 +1.97412500000000016 -0.48150634765625 -0.385345458984375 -0.1996826171874888 +1.97425 -0.480072021484375 -0.384185791015625 -0.1996826171874888 +1.974375 -0.480072021484375 -0.384185791015625 -0.1996826171874888 +1.97450000000000016 -0.478607177734375 -0.38299560546875 -0.1996826171874888 +1.974625 -0.478607177734375 -0.38299560546875 -0.1996826171874888 +1.97475 -0.47705078125 -0.38177490234375 -0.1996826171874888 +1.974875 -0.4754638671875 -0.3804931640625 -0.1996826171874888 +1.975 -0.4754638671875 -0.3804931640625 -0.1996826171874888 +1.975125 -0.47381591796875 -0.379180908203125 -0.1996826171874888 +1.97525 -0.47381591796875 -0.379180908203125 -0.1996826171874888 +1.975375 -0.472137451171875 -0.377838134765625 -0.1996826171874888 +1.9755 -0.470367431640625 -0.376434326171875 -0.1996826171874888 +1.975625 -0.470367431640625 -0.376434326171875 -0.1996826171874888 +1.97575000000000016 -0.46856689453125 -0.374969482421875 -0.1996826171874888 +1.975875 -0.46856689453125 -0.374969482421875 -0.1996826171874888 +1.976 -0.46673583984375 -0.373504638671875 -0.1996826171874888 +1.97612500000000016 -0.464813232421875 -0.371978759765625 -0.1996826171874888 +1.97625 -0.464813232421875 -0.371978759765625 -0.1996826171874888 +1.976375 -0.462890625 -0.37042236328125 -0.1996826171874888 +1.976500000000000256 -0.462890625 -0.37042236328125 -0.1996826171874888 +1.976625 -0.46087646484375 -0.368804931640625 -0.1996826171874888 +1.97675 -0.458831787109375 -0.3671875 -0.1996826171874888 +1.976875 -0.458831787109375 -0.3671875 -0.1996826171874888 +1.977 -0.45672607421875 -0.365509033203125 -0.1996826171874888 +1.977125 -0.45672607421875 -0.365509033203125 -0.1996826171874888 +1.97725 -0.454559326171875 -0.36376953125 -0.1996826171874888 +1.977375 -0.452362060546875 -0.36199951171875 -0.1996826171874888 +1.9775 -0.452362060546875 -0.36199951171875 -0.1996826171874888 +1.977625 -0.450103759765625 -0.360198974609375 -0.1996826171874888 +1.97775000000000016 -0.450103759765625 -0.360198974609375 -0.1996826171874888 +1.977875 -0.447784423828125 -0.358367919921875 -0.1996826171874888 +1.978 -0.4454345703125 -0.356475830078125 -0.1996826171874888 +1.97812500000000016 -0.4454345703125 -0.356475830078125 -0.1996826171874888 +1.97825 -0.44305419921875 -0.35455322265625 -0.1996826171874888 +1.978375 -0.44305419921875 -0.35455322265625 -0.1996826171874888 +1.978500000000000256 -0.440582275390625 -0.35260009765625 -0.1996826171874888 +1.978625 -0.438079833984375 -0.3505859375 -0.1996826171874888 +1.97875 -0.438079833984375 -0.3505859375 -0.1996826171874888 +1.978875 -0.435546875 -0.34857177734375 -0.1996826171874888 +1.979 -0.435546875 -0.34857177734375 -0.1996826171874888 +1.979125 -0.432952880859375 -0.34649658203125 -0.1996826171874888 +1.97925 -0.4302978515625 -0.3443603515625 -0.1996826171874888 +1.979375 -0.4302978515625 -0.3443603515625 -0.1996826171874888 +1.9795 -0.4276123046875 -0.34222412109375 -0.1996826171874888 +1.979625 -0.4276123046875 -0.34222412109375 -0.1996826171874888 +1.97975000000000016 -0.424896240234375 -0.34002685546875 -0.1996826171874888 +1.979875 -0.422119140625 -0.337799072265625 -0.1996826171874888 +1.98 -0.422119140625 -0.337799072265625 -0.1996826171874888 +1.98012500000000016 -0.419281005859375 -0.335540771484375 -0.1996826171874888 +1.98025 -0.419281005859375 -0.335540771484375 -0.1996826171874888 +1.980375 -0.416412353515625 -0.333221435546875 -0.1996826171874888 +1.980500000000000256 -0.413482666015625 -0.330902099609375 -0.1996826171874888 +1.980625 -0.413482666015625 -0.330902099609375 -0.1996826171874888 +1.98075 -0.4105224609375 -0.328521728515625 -0.1996826171874888 +1.980875 -0.4105224609375 -0.328521728515625 -0.1996826171874888 +1.981 -0.407501220703125 -0.32611083984375 -0.1996826171874888 +1.981125 -0.404449462890625 -0.32366943359375 -0.1996826171874888 +1.98125 -0.404449462890625 -0.32366943359375 -0.1996826171874888 +1.981375 -0.401336669921875 -0.321197509765625 -0.1996826171874888 +1.9815 -0.401336669921875 -0.321197509765625 -0.1996826171874888 +1.981625 -0.398193359375 -0.31866455078125 -0.1996826171874888 +1.98175000000000016 -0.39501953125 -0.316131591796875 -0.1996826171874888 +1.981875 -0.39501953125 -0.316131591796875 -0.1996826171874888 +1.982 -0.39178466796875 -0.31353759765625 -0.1996826171874888 +1.98212500000000016 -0.39178466796875 -0.31353759765625 -0.1996826171874888 +1.98225 -0.388519287109375 -0.3109130859375 -0.1996826171874888 +1.982375 -0.38519287109375 -0.308258056640625 -0.1996826171874888 +1.982500000000000256 -0.38519287109375 -0.308258056640625 -0.1996826171874888 +1.982625 -0.3818359375 -0.305572509765625 -0.1996826171874888 +1.98275 -0.3818359375 -0.305572509765625 -0.1996826171874888 +1.982875 -0.378448486328125 -0.3028564453125 -0.1996826171874888 +1.983 -0.375 -0.30010986328125 -0.1996826171874888 +1.983125 -0.375 -0.30010986328125 -0.1996826171874888 +1.98325 -0.37152099609375 -0.29730224609375 -0.1996826171874888 +1.983375 -0.37152099609375 -0.29730224609375 -0.1996826171874888 +1.9835 -0.36798095703125 -0.29449462890625 -0.1996826171874888 +1.983625 -0.36444091796875 -0.291656494140625 -0.1996826171874888 +1.98375000000000016 -0.36444091796875 -0.291656494140625 -0.1996826171874888 +1.983875 -0.360809326171875 -0.28875732421875 -0.1996826171874888 +1.984 -0.28875732421875 -0.360809326171875 0.1996826171875111 +1.98412500000000016 -0.285858154296875 -0.357177734375 0.1996826171875111 +1.98425 -0.28289794921875 -0.353485107421875 0.1996826171875111 +1.984375 -0.28289794921875 -0.353485107421875 0.1996826171875111 +1.9845 -0.2799072265625 -0.349761962890625 0.1996826171875111 +1.984625 -0.2799072265625 -0.349761962890625 0.1996826171875111 +1.98475 -0.27691650390625 -0.34600830078125 0.1996826171875111 +1.984875 -0.27386474609375 -0.34222412109375 0.1996826171875111 +1.985 -0.27386474609375 -0.34222412109375 0.1996826171875111 +1.985125 -0.27081298828125 -0.33837890625 0.1996826171875111 +1.98525 -0.27081298828125 -0.33837890625 0.1996826171875111 +1.98537500000000016 -0.2677001953125 -0.334503173828125 0.1996826171875111 +1.9855 -0.26458740234375 -0.330596923828125 0.1996826171875111 +1.985625 -0.26458740234375 -0.330596923828125 0.1996826171875111 +1.98575000000000016 -0.26141357421875 -0.32666015625 0.1996826171875111 +1.985875 -0.26141357421875 -0.32666015625 0.1996826171875111 +1.986 -0.25823974609375 -0.322662353515625 0.1996826171875111 +1.98612500000000016 -0.2550048828125 -0.31866455078125 0.1996826171875111 +1.98625 -0.2550048828125 -0.31866455078125 0.1996826171875111 +1.986375 -0.25177001953125 -0.314605712890625 0.1996826171875111 +1.9865 -0.25177001953125 -0.314605712890625 0.1996826171875111 +1.986625 -0.248504638671875 -0.310516357421875 0.1996826171875111 +1.98675 -0.245208740234375 -0.306396484375 0.1996826171875111 +1.986875 -0.245208740234375 -0.306396484375 0.1996826171875111 +1.987 -0.24188232421875 -0.30224609375 0.1996826171875111 +1.987125 -0.24188232421875 -0.30224609375 0.1996826171875111 +1.98725 -0.238525390625 -0.298065185546875 0.1996826171875111 +1.98737500000000016 -0.235137939453125 -0.2938232421875 0.1996826171875111 +1.9875 -0.235137939453125 -0.2938232421875 0.1996826171875111 +1.987625 -0.23175048828125 -0.289581298828125 0.1996826171875111 +1.98775000000000016 -0.23175048828125 -0.289581298828125 0.1996826171875111 +1.987875 -0.22833251953125 -0.285308837890625 0.1996826171875111 +1.988 -0.224853515625 -0.280975341796875 0.1996826171875111 +1.98812500000000016 -0.224853515625 -0.280975341796875 0.1996826171875111 +1.98825 -0.22137451171875 -0.276641845703125 0.1996826171875111 +1.988375 -0.22137451171875 -0.276641845703125 0.1996826171875111 +1.9885 -0.2178955078125 -0.27227783203125 0.1996826171875111 +1.988625 -0.21435546875 -0.267852783203125 0.1996826171875111 +1.98875 -0.21435546875 -0.267852783203125 0.1996826171875111 +1.988875 -0.2108154296875 -0.263427734375 0.1996826171875111 +1.989 -0.2108154296875 -0.263427734375 0.1996826171875111 +1.989125 -0.207244873046875 -0.25897216796875 0.1996826171875111 +1.98925 -0.203643798828125 -0.25445556640625 0.1996826171875111 +1.98937500000000016 -0.203643798828125 -0.25445556640625 0.1996826171875111 +1.9895 -0.200042724609375 -0.249969482421875 0.1996826171875111 +1.989625 -0.200042724609375 -0.249969482421875 0.1996826171875111 +1.98975000000000016 -0.196380615234375 -0.24542236328125 0.1996826171875111 +1.989875 -0.192718505859375 -0.2408447265625 0.1996826171875111 +1.99 -0.192718505859375 -0.2408447265625 0.1996826171875111 +1.99012500000000016 -0.189056396484375 -0.236236572265625 0.1996826171875111 +1.99025 -0.189056396484375 -0.236236572265625 0.1996826171875111 +1.990375 -0.185333251953125 -0.231597900390625 0.1996826171875111 +1.9905 -0.181610107421875 -0.226959228515625 0.1996826171875111 +1.990625 -0.181610107421875 -0.226959228515625 0.1996826171875111 +1.99075 -0.177886962890625 -0.2222900390625 0.1996826171875111 +1.990875 -0.177886962890625 -0.2222900390625 0.1996826171875111 +1.991 -0.17413330078125 -0.21759033203125 0.1996826171875111 +1.991125 -0.17034912109375 -0.212860107421875 0.1996826171875111 +1.99125 -0.17034912109375 -0.212860107421875 0.1996826171875111 +1.99137500000000016 -0.166534423828125 -0.208099365234375 0.1996826171875111 +1.9915 -0.166534423828125 -0.208099365234375 0.1996826171875111 +1.991625 -0.1627197265625 -0.203338623046875 0.1996826171875111 +1.99175000000000016 -0.15887451171875 -0.19854736328125 0.1996826171875111 +1.991875 -0.15887451171875 -0.19854736328125 0.1996826171875111 +1.992 -0.155029296875 -0.1937255859375 0.1996826171875111 +1.992125000000000256 -0.155029296875 -0.1937255859375 0.1996826171875111 +1.99225 -0.151153564453125 -0.188873291015625 0.1996826171875111 +1.992375 -0.14727783203125 -0.18402099609375 0.1996826171875111 +1.9925 -0.14727783203125 -0.18402099609375 0.1996826171875111 +1.992625 -0.14337158203125 -0.17913818359375 0.1996826171875111 +1.99275 -0.14337158203125 -0.17913818359375 0.1996826171875111 +1.992875 -0.139434814453125 -0.17425537109375 0.1996826171875111 +1.993 -0.135498046875 -0.169342041015625 0.1996826171875111 +1.993125 -0.135498046875 -0.169342041015625 0.1996826171875111 +1.99325 -0.131561279296875 -0.164398193359375 0.1996826171875111 +1.99337500000000016 -0.131561279296875 -0.164398193359375 0.1996826171875111 +1.9935 -0.127593994140625 -0.159454345703125 0.1996826171875111 +1.993625 -0.123626708984375 -0.15447998046875 0.1996826171875111 +1.99375000000000016 -0.123626708984375 -0.15447998046875 0.1996826171875111 +1.993875 -0.11962890625 -0.14947509765625 0.1996826171875111 +1.994 -0.11962890625 -0.14947509765625 0.1996826171875111 +1.994125000000000256 -0.115631103515625 -0.14447021484375 0.1996826171875111 +1.99425 -0.111602783203125 -0.13946533203125 0.1996826171875111 +1.994375 -0.111602783203125 -0.13946533203125 0.1996826171875111 +1.9945 -0.107574462890625 -0.134429931640625 0.1996826171875111 +1.994625 -0.107574462890625 -0.134429931640625 0.1996826171875111 +1.99475 -0.103515625 -0.129364013671875 0.1996826171875111 +1.994875 -0.0994873046875 -0.124298095703125 0.1996826171875111 +1.995 -0.0994873046875 -0.124298095703125 0.1996826171875111 +1.995125 -0.095428466796875 -0.119232177734375 0.1996826171875111 +1.99525 -0.095428466796875 -0.119232177734375 0.1996826171875111 +1.99537500000000016 -0.091339111328125 -0.1141357421875 0.1996826171875111 +1.9955 -0.087249755859375 -0.109039306640625 0.1996826171875111 +1.995625 -0.087249755859375 -0.109039306640625 0.1996826171875111 +1.99575000000000016 -0.083160400390625 -0.103912353515625 0.1996826171875111 +1.995875 -0.083160400390625 -0.103912353515625 0.1996826171875111 +1.996 -0.079071044921875 -0.098785400390625 0.1996826171875111 +1.996125000000000256 -0.074951171875 -0.093658447265625 0.1996826171875111 +1.99625 -0.074951171875 -0.093658447265625 0.1996826171875111 +1.996375 -0.070831298828125 -0.0885009765625 0.1996826171875111 +1.9965 -0.070831298828125 -0.0885009765625 0.1996826171875111 +1.996625 -0.06671142578125 -0.083343505859375 0.1996826171875111 +1.99675 -0.06256103515625 -0.07818603515625 0.1996826171875111 +1.996875 -0.06256103515625 -0.07818603515625 0.1996826171875111 +1.997 -0.05841064453125 -0.072998046875 0.1996826171875111 +1.997125 -0.05841064453125 -0.072998046875 0.1996826171875111 +1.99725 -0.05426025390625 -0.06781005859375 0.1996826171875111 +1.99737500000000016 -0.05010986328125 -0.0626220703125 0.1996826171875111 +1.9975 -0.05010986328125 -0.0626220703125 0.1996826171875111 +1.997625 -0.04595947265625 -0.05743408203125 0.1996826171875111 +1.99775000000000016 -0.04595947265625 -0.05743408203125 0.1996826171875111 +1.997875 -0.04180908203125 -0.05224609375 0.1996826171875111 +1.998 -0.037628173828125 -0.047027587890625 0.1996826171875111 +1.998125000000000256 -0.037628173828125 -0.047027587890625 0.1996826171875111 +1.99825 -0.033447265625 -0.04180908203125 0.1996826171875111 +1.998375 -0.033447265625 -0.04180908203125 0.1996826171875111 +1.9985 -0.029266357421875 -0.036590576171875 0.1996826171875111 +1.998625 -0.025115966796875 -0.0313720703125 0.1996826171875111 +1.99875 -0.025115966796875 -0.0313720703125 0.1996826171875111 +1.998875 -0.020904541015625 -0.026123046875 0.1996826171875111 +1.999 -0.020904541015625 -0.026123046875 0.1996826171875111 +1.999125 -0.0167236328125 -0.020904541015625 0.1996826171875111 +1.99925 -0.012542724609375 -0.01568603515625 0.1996826171875111 +1.99937500000000016 -0.012542724609375 -0.01568603515625 0.1996826171875111 +1.9995 -0.00836181640625 -0.01043701171875 0.1996826171875111 +1.999625 -0.00836181640625 -0.01043701171875 0.1996826171875111 +1.99975000000000016 -0.004180908203125 -0.005218505859375 0.1996826171875111 +1.999875 0.0 0.0 0.1996826171875111 +2.0 0.0 0.0 0.1996826171875111 +2.000125 0.004180908203125 0.005218505859375 0.1996826171875111 +2.00025 0.004180908203125 0.005218505859375 0.1996826171875111 +2.000375 0.00836181640625 0.01043701171875 0.1996826171875111 +2.0005 0.012542724609375 0.01568603515625 0.1996826171875111 +2.000625 0.012542724609375 0.01568603515625 0.1996826171875111 +2.00075 0.0167236328125 0.020904541015625 0.1996826171875111 +2.000875 0.0167236328125 0.020904541015625 0.1996826171875111 +2.001 0.020904541015625 0.026123046875 0.1996826171875111 +2.001125 0.025115966796875 0.0313720703125 0.1996826171875111 +2.00125 0.025115966796875 0.0313720703125 0.1996826171875111 +2.001375 0.029266357421875 0.036590576171875 0.1996826171875111 +2.0015 0.029266357421875 0.036590576171875 0.1996826171875111 +2.001625 0.033447265625 0.04180908203125 0.1996826171875111 +2.00175 0.037628173828125 0.047027587890625 0.1996826171875111 +2.001875 0.037628173828125 0.047027587890625 0.1996826171875111 +2.0020000000000004 0.04180908203125 0.05224609375 0.1996826171875111 +2.002125 0.04180908203125 0.05224609375 0.1996826171875111 +2.00225 0.04595947265625 0.05743408203125 0.1996826171875111 +2.0023750000000004 0.05010986328125 0.0626220703125 0.1996826171875111 +2.0025 0.05010986328125 0.0626220703125 0.1996826171875111 +2.002625 0.05426025390625 0.06781005859375 0.1996826171875111 +2.0027500000000004 0.05426025390625 0.06781005859375 0.1996826171875111 +2.002875 0.05841064453125 0.072998046875 0.1996826171875111 +2.003 0.06256103515625 0.07818603515625 0.1996826171875111 +2.003125 0.06256103515625 0.07818603515625 0.1996826171875111 +2.00325 0.06671142578125 0.083343505859375 0.1996826171875111 +2.003375 0.06671142578125 0.083343505859375 0.1996826171875111 +2.0035 0.070831298828125 0.0885009765625 0.1996826171875111 +2.003625 0.074951171875 0.093658447265625 0.1996826171875111 +2.00375 0.074951171875 0.093658447265625 0.1996826171875111 +2.003875 0.079071044921875 0.098785400390625 0.1996826171875111 +2.004 0.079071044921875 0.098785400390625 0.1996826171875111 +2.004125 0.083160400390625 0.103912353515625 0.1996826171875111 +2.00425 0.087249755859375 0.109039306640625 0.1996826171875111 +2.004375 0.087249755859375 0.109039306640625 0.1996826171875111 +2.0045 0.091339111328125 0.1141357421875 0.1996826171875111 +2.004625 0.091339111328125 0.1141357421875 0.1996826171875111 +2.00475 0.095428466796875 0.119232177734375 0.1996826171875111 +2.004875 0.0994873046875 0.124298095703125 0.1996826171875111 +2.005 0.0994873046875 0.124298095703125 0.1996826171875111 +2.005125 0.103515625 0.129364013671875 0.1996826171875111 +2.00525 0.103515625 0.129364013671875 0.1996826171875111 +2.005375 0.107574462890625 0.134429931640625 0.1996826171875111 +2.0055 0.111602783203125 0.13946533203125 0.1996826171875111 +2.005625 0.111602783203125 0.13946533203125 0.1996826171875111 +2.00575 0.115631103515625 0.14447021484375 0.1996826171875111 +2.005875 0.115631103515625 0.14447021484375 0.1996826171875111 +2.0060000000000004 0.11962890625 0.14947509765625 0.1996826171875111 +2.006125 0.123626708984375 0.15447998046875 0.1996826171875111 +2.00625 0.123626708984375 0.15447998046875 0.1996826171875111 +2.0063750000000004 0.127593994140625 0.159454345703125 0.1996826171875111 +2.0065 0.127593994140625 0.159454345703125 0.1996826171875111 +2.006625 0.131561279296875 0.164398193359375 0.1996826171875111 +2.0067500000000004 0.135498046875 0.169342041015625 0.1996826171875111 +2.006875 0.135498046875 0.169342041015625 0.1996826171875111 +2.007 0.139434814453125 0.17425537109375 0.1996826171875111 +2.007125 0.139434814453125 0.17425537109375 0.1996826171875111 +2.00725 0.14337158203125 0.17913818359375 0.1996826171875111 +2.007375 0.14727783203125 0.18402099609375 0.1996826171875111 +2.0075 0.14727783203125 0.18402099609375 0.1996826171875111 +2.007625 0.151153564453125 0.188873291015625 0.1996826171875111 +2.00775 0.151153564453125 0.188873291015625 0.1996826171875111 +2.007875 0.155029296875 0.1937255859375 0.1996826171875111 +2.008 0.15887451171875 0.19854736328125 0.1996826171875111 +2.008125 0.15887451171875 0.19854736328125 0.1996826171875111 +2.00825 0.1627197265625 0.203338623046875 0.1996826171875111 +2.008375 0.1627197265625 0.203338623046875 0.1996826171875111 +2.0085 0.166534423828125 0.208099365234375 0.1996826171875111 +2.008625 0.17034912109375 0.212860107421875 0.1996826171875111 +2.00875 0.17034912109375 0.212860107421875 0.1996826171875111 +2.008875 0.17413330078125 0.21759033203125 0.1996826171875111 +2.009 0.17413330078125 0.21759033203125 0.1996826171875111 +2.009125 0.177886962890625 0.2222900390625 0.1996826171875111 +2.00925 0.181610107421875 0.226959228515625 0.1996826171875111 +2.009375 0.181610107421875 0.226959228515625 0.1996826171875111 +2.0095 0.185333251953125 0.231597900390625 0.1996826171875111 +2.009625 0.185333251953125 0.231597900390625 0.1996826171875111 +2.00975 0.189056396484375 0.236236572265625 0.1996826171875111 +2.009875 0.192718505859375 0.2408447265625 0.1996826171875111 +2.0100000000000004 0.192718505859375 0.2408447265625 0.1996826171875111 +2.010125 0.196380615234375 0.24542236328125 0.1996826171875111 +2.01025 0.196380615234375 0.24542236328125 0.1996826171875111 +2.0103750000000004 0.200042724609375 0.249969482421875 0.1996826171875111 +2.0105 0.203643798828125 0.25445556640625 0.1996826171875111 +2.010625 0.203643798828125 0.25445556640625 0.1996826171875111 +2.0107500000000004 0.207244873046875 0.25897216796875 0.1996826171875111 +2.010875 0.207244873046875 0.25897216796875 0.1996826171875111 +2.011 0.2108154296875 0.263427734375 0.1996826171875111 +2.011125 0.21435546875 0.267852783203125 0.1996826171875111 +2.01125 0.21435546875 0.267852783203125 0.1996826171875111 +2.011375 0.2178955078125 0.27227783203125 0.1996826171875111 +2.0115 0.2178955078125 0.27227783203125 0.1996826171875111 +2.011625 0.22137451171875 0.276641845703125 0.1996826171875111 +2.01175 0.224853515625 0.280975341796875 0.1996826171875111 +2.011875 0.224853515625 0.280975341796875 0.1996826171875111 +2.012 0.22833251953125 0.285308837890625 0.1996826171875111 +2.012125 0.22833251953125 0.285308837890625 0.1996826171875111 +2.01225 0.23175048828125 0.289581298828125 0.1996826171875111 +2.012375 0.235137939453125 0.2938232421875 0.1996826171875111 +2.0125 0.235137939453125 0.2938232421875 0.1996826171875111 +2.012625 0.238525390625 0.298065185546875 0.1996826171875111 +2.01275 0.238525390625 0.298065185546875 0.1996826171875111 +2.012875 0.24188232421875 0.30224609375 0.1996826171875111 +2.013 0.245208740234375 0.306396484375 0.1996826171875111 +2.013125 0.245208740234375 0.306396484375 0.1996826171875111 +2.01325 0.248504638671875 0.310516357421875 0.1996826171875111 +2.013375 0.248504638671875 0.310516357421875 0.1996826171875111 +2.0135 0.25177001953125 0.314605712890625 0.1996826171875111 +2.013625 0.2550048828125 0.31866455078125 0.1996826171875111 +2.01375 0.2550048828125 0.31866455078125 0.1996826171875111 +2.013875 0.25823974609375 0.322662353515625 0.1996826171875111 +2.0140000000000004 0.25823974609375 0.322662353515625 0.1996826171875111 +2.014125 0.26141357421875 0.32666015625 0.1996826171875111 +2.01425 0.26458740234375 0.330596923828125 0.1996826171875111 +2.0143750000000004 0.26458740234375 0.330596923828125 0.1996826171875111 +2.0145 0.2677001953125 0.334503173828125 0.1996826171875111 +2.014625 0.2677001953125 0.334503173828125 0.1996826171875111 +2.0147500000000004 0.27081298828125 0.33837890625 0.1996826171875111 +2.014875 0.27386474609375 0.34222412109375 0.1996826171875111 +2.015 0.27386474609375 0.34222412109375 0.1996826171875111 +2.015125 0.27691650390625 0.34600830078125 0.1996826171875111 +2.01525 0.27691650390625 0.34600830078125 0.1996826171875111 +2.015375 0.2799072265625 0.349761962890625 0.1996826171875111 +2.0155 0.28289794921875 0.353485107421875 0.1996826171875111 +2.015625 0.28289794921875 0.353485107421875 0.1996826171875111 +2.01575 0.285858154296875 0.357177734375 0.1996826171875111 +2.015875 0.285858154296875 0.357177734375 0.1996826171875111 diff --git a/tests/circuitpython/traceback_test.py b/tests/circuitpython/traceback_test.py index 17c54c857a9..71888437a22 100644 --- a/tests/circuitpython/traceback_test.py +++ b/tests/circuitpython/traceback_test.py @@ -23,6 +23,22 @@ def fun(): print("\nLimit=-1 Trace:") print("".join(traceback.format_exception(None, exc, exc.__traceback__, limit=-1)), end="") +# value and tb must both be supplied or neither +print() +try: + fun() +except Exception as exc: + try: + traceback.print_exception(None, value=exc) + print("Should have raised ValueError for missing tb arg") + except ValueError: + print("ValueError for missing tb arg, as expected") + try: + traceback.print_exception(None, tb=None) + print("Should have raised ValueError for missing value arg") + except ValueError: + print("ValueError for missing value arg, as expected") + class NonNativeException(Exception): pass diff --git a/tests/run-tests.py b/tests/run-tests.py index 71570292ce7..d2849efa360 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -418,8 +418,9 @@ def detect_test_platform(pyb, args): output = run_feature_check(pyb, args, "target_info.py") if output.endswith(b"CRASH"): raise ValueError("cannot detect platform: {}".format(output)) + # CIRCUITPY-CHANGE: sys.platform is the first field and can contain spaces ("Atmel SAMD21"). platform, arch, arch_flags, build, thread, float_prec, unicode = ( - str(output, "ascii").strip().split() + str(output, "ascii").strip().rsplit(None, 6) ) if arch == "None": arch = None diff --git a/tools/ci_changes_per_commit.py b/tools/ci_changes_per_commit.py index 6cbd475fa95..71cfb36cf27 100644 --- a/tools/ci_changes_per_commit.py +++ b/tools/ci_changes_per_commit.py @@ -155,6 +155,11 @@ def get_commit_depth_and_check_suite(query_commits): check_suites = commit["checkSuites"] if check_suites["totalCount"] > 0: for check_suite in check_suites["nodes"]: + # workflowRun is null for check suites not attached to + # a workflow run, such as one that was deleted or one + # belonging to an app integration. + if check_suite["workflowRun"] is None: + continue if check_suite["workflowRun"]["workflow"]["name"] == "Build CI": return [ {"sha": commit_sha, "depth": commit_depth}, diff --git a/tools/ci_check_duplicate_usb_vid_pid.py b/tools/ci_check_duplicate_usb_vid_pid.py index 38cce0f28be..66a9fb4c5a3 100644 --- a/tools/ci_check_duplicate_usb_vid_pid.py +++ b/tools/ci_check_duplicate_usb_vid_pid.py @@ -58,6 +58,7 @@ "espressif_esp32s3_devkitc_1_n8r8_hacktablet", "espressif_esp32s3_devkitc_1_n16", "espressif_esp32s3_devkitc_1_n32r8", + "espressif_esp32s3_devkitc_1_n32r16", ], "0x303A:0x7009": [ "espressif_esp32s2_devkitc_1_n4",